From 45d0192f825a1e14334d98c9ea846b1a15ff5ed6 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 28 Apr 2022 16:41:07 +0200 Subject: [PATCH] Copy jslib into Directory Connector [TI-6] (#262) --- .eslintignore | 3 +- .gitmodules | 4 - .prettierignore | 2 - jslib | 1 - jslib/.gitignore | 9 + jslib/.prettierrc.json | 3 + jslib/angular/jest.config.js | 17 + jslib/angular/package-lock.json | 654 + jslib/angular/package.json | 41 + jslib/angular/spec/test.ts | 28 + .../src/components/callout.component.html | 35 + .../src/components/callout.component.ts | 78 + .../src/components/environment.component.ts | 63 + .../src/components/icon.component.html | 11 + .../angular/src/components/icon.component.ts | 112 + .../modal/dynamic-modal.component.ts | 79 + .../src/components/modal/modal-injector.ts | 10 + .../angular/src/components/modal/modal.ref.ts | 50 + .../components/password-reprompt.component.ts | 41 + .../src/components/toastr.component.ts | 95 + .../user-verification.component.html | 46 + .../components/user-verification.component.ts | 96 + .../src/directives/a11y-title.directive.ts | 23 + .../src/directives/api-action.directive.ts | 49 + .../src/directives/autofocus.directive.ts | 27 + .../src/directives/blur-click.directive.ts | 12 + .../src/directives/box-row.directive.ts | 59 + .../src/directives/fallback-src.directive.ts | 14 + .../src/directives/stop-click.directive.ts | 10 + .../src/directives/stop-prop.directive.ts | 10 + jslib/angular/src/pipes/i18n.pipe.ts | 14 + .../angular/src/pipes/search-ciphers.pipe.ts | 41 + .../src/scss/bwicons/fonts/bwi-font.svg | 170 + .../src/scss/bwicons/fonts/bwi-font.ttf | Bin 0 -> 68412 bytes .../src/scss/bwicons/fonts/bwi-font.woff | Bin 0 -> 68488 bytes .../src/scss/bwicons/fonts/bwi-font.woff2 | Bin 0 -> 29820 bytes .../src/scss/bwicons/styles/style.scss | 250 + jslib/angular/src/scss/icons.scss | 44 + jslib/angular/src/scss/webfonts.css | 89 + .../scss/webfonts/Open_Sans-italic-300.woff | Bin 0 -> 53108 bytes .../scss/webfonts/Open_Sans-italic-400.woff | Bin 0 -> 53108 bytes .../scss/webfonts/Open_Sans-italic-600.woff | Bin 0 -> 54100 bytes .../scss/webfonts/Open_Sans-italic-700.woff | Bin 0 -> 53000 bytes .../scss/webfonts/Open_Sans-italic-800.woff | Bin 0 -> 72092 bytes .../scss/webfonts/Open_Sans-normal-300.woff | Bin 0 -> 57032 bytes .../scss/webfonts/Open_Sans-normal-400.woff | Bin 0 -> 55324 bytes .../scss/webfonts/Open_Sans-normal-600.woff | Bin 0 -> 57744 bytes .../scss/webfonts/Open_Sans-normal-700.woff | Bin 0 -> 58016 bytes .../scss/webfonts/Open_Sans-normal-800.woff | Bin 0 -> 57664 bytes .../src/services/auth-guard.service.ts | 45 + .../src/services/broadcaster.service.ts | 6 + .../src/services/jslib-services.module.ts | 492 + .../src/services/lock-guard.service.ts | 29 + jslib/angular/src/services/modal.service.ts | 180 + .../src/services/passwordReprompt.service.ts | 45 + .../src/services/unauth-guard.service.ts | 29 + .../src/services/validation.service.ts | 38 + jslib/angular/tsconfig.json | 11 + jslib/angular/tsconfig.spec.json | 3 + jslib/common/jest.config.js | 18 + jslib/common/package-lock.json | 956 + jslib/common/package.json | 42 + jslib/common/spec/domain/attachment.spec.ts | 83 + jslib/common/spec/domain/card.spec.ts | 73 + jslib/common/spec/domain/cipher.spec.ts | 599 + jslib/common/spec/domain/collection.spec.ts | 66 + jslib/common/spec/domain/encString.spec.ts | 195 + jslib/common/spec/domain/field.spec.ts | 64 + jslib/common/spec/domain/folder.spec.ts | 42 + jslib/common/spec/domain/identity.spec.ts | 134 + jslib/common/spec/domain/login.spec.ts | 101 + jslib/common/spec/domain/loginUri.spec.ts | 57 + jslib/common/spec/domain/password.spec.ts | 51 + jslib/common/spec/domain/secureNote.spec.ts | 46 + jslib/common/spec/domain/send.spec.ts | 144 + jslib/common/spec/domain/sendAccess.spec.ts | 84 + jslib/common/spec/domain/sendFile.spec.ts | 57 + jslib/common/spec/domain/sendText.spec.ts | 47 + .../spec/domain/symmetricCryptoKey.spec.ts | 69 + .../importers/bitwardenJsonImporter.spec.ts | 31 + ...bitwardenPasswordProtectedImporter.spec.ts | 113 + .../importers/dashlaneCsvImporter.spec.ts | 367 + .../spec/importers/firefoxCsvImporter.spec.ts | 74 + .../spec/importers/fsecureFskImporter.spec.ts | 77 + .../importers/keepass2XmlImporter.spec.ts | 189 + .../spec/importers/keeperJsonImporter.spec.ts | 108 + .../importers/lastpassCsvImporter.spec.ts | 202 + .../spec/importers/mykiCsvImporter.spec.ts | 633 + .../importers/nordpassCsvImporter.spec.ts | 181 + .../importers/onepassword1PifImporter.spec.ts | 527 + .../importers/onepassword1PuxImporter.spec.ts | 689 + .../onepasswordMacCsvImporter.spec.ts | 74 + .../onepasswordWinCsvImporter.spec.ts | 87 + .../spec/importers/safariCsvImporter.spec.ts | 74 + .../testData/bitwardenJson/empty.json.ts | 1 + .../bitwardenJson/passwordProtected.json.ts | 9 + .../testData/dashlaneCsv/credentials.csv.ts | 2 + .../importers/testData/dashlaneCsv/id.csv.ts | 6 + .../dashlaneCsv/multiplePersonalInfo.csv.ts | 7 + .../testData/dashlaneCsv/payments.csv.ts | 3 + .../testData/dashlaneCsv/personalInfo.csv.ts | 6 + .../testData/dashlaneCsv/securenotes.csv.ts | 2 + .../firefoxCsv/firefoxAccountsData.csv.ts | 4 + .../firefoxCsv/simplePasswordData.csv.ts | 2 + .../importers/testData/keeperJson/testData.ts | 90 + .../testData/mykiCsv/UserAccount.csv.ts | 3 + .../testData/mykiCsv/UserCreditCard.csv.ts | 3 + .../testData/mykiCsv/UserIdCard.csv.ts | 16 + .../testData/mykiCsv/UserIdentity.csv.ts | 3 + .../testData/mykiCsv/UserNote.csv.ts | 3 + .../testData/mykiCsv/UserTwofa.csv.ts | 3 + .../testData/nordpassCsv/nordpass.card.csv.ts | 2 + .../nordpassCsv/nordpass.identity.csv.ts | 2 + .../nordpassCsv/nordpass.login.csv.ts | 2 + .../nordpassCsv/nordpass.secureNote.csv.ts | 3 + .../onePassword1Pux/APICredentials.ts | 169 + .../testData/onePassword1Pux/BankAccount.ts | 224 + .../testData/onePassword1Pux/CreditCard.ts | 343 + .../testData/onePassword1Pux/Database.ts | 201 + .../onePassword1Pux/DriversLicense.ts | 233 + .../testData/onePassword1Pux/EmailAccount.ts | 341 + .../testData/onePassword1Pux/Emailfield.ts | 91 + .../onePassword1Pux/EmailfieldOnIdentity.ts | 87 + .../EmailfieldOnIdentity_Prefilled.ts | 103 + .../testData/onePassword1Pux/IdentityData.ts | 476 + .../testData/onePassword1Pux/LoginData.ts | 135 + .../testData/onePassword1Pux/MedicalRecord.ts | 198 + .../testData/onePassword1Pux/Membership.ts | 186 + .../onePassword1Pux/Onepux_example.ts | 93 + .../onePassword1Pux/OutdoorLicense.ts | 169 + .../testData/onePassword1Pux/Passport.ts | 234 + .../testData/onePassword1Pux/Password.ts | 58 + .../onePassword1Pux/RewardsProgram.ts | 209 + .../importers/testData/onePassword1Pux/SSN.ts | 89 + .../onePassword1Pux/SanitizedExport.ts | 4519 ++ .../testData/onePassword1Pux/SecureNote.ts | 52 + .../testData/onePassword1Pux/Server.ts | 229 + .../onePassword1Pux/SoftwareLicense.ts | 289 + .../onePassword1Pux/WirelessRouter.ts | 182 + .../onePasswordCsv/creditCard.mac.csv.ts | 4 + .../onePasswordCsv/creditCard.windows.csv.ts | 2 + .../onePasswordCsv/identity.mac.csv.ts | 7 + .../onePasswordCsv/identity.windows.csv.ts | 2 + .../onePasswordCsv/multipleItems.mac.csv.ts | 15 + .../multipleItems.windows.csv.ts | 5 + .../safariCsv/oldSimplePasswordData.csv.ts | 2 + .../safariCsv/simplePasswordData.csv.ts | 3 + .../testData/safeInCloud/testData.xml.ts | 63 + .../logInStrategies/apiLogIn.strategy.spec.ts | 114 + .../logInStrategies/logIn.strategy.spec.ts | 288 + .../passwordLogIn.strategy.spec.ts | 110 + .../logInStrategies/ssoLogIn.strategy.spec.ts | 127 + jslib/common/spec/misc/sequentialize.spec.ts | 127 + jslib/common/spec/misc/throttle.spec.ts | 110 + jslib/common/spec/misc/utils.spec.ts | 73 + .../spec/services/cipher.service.spec.ts | 69 + .../spec/services/consoleLog.service.spec.ts | 102 + .../spec/services/export.service.spec.ts | 209 + .../spec/services/import.service.spec.ts | 72 + .../spec/services/stateMigration.service.ts | 84 + jslib/common/spec/test.ts | 5 + jslib/common/spec/utils.ts | 37 + .../webCryptoFunction.service.spec.ts | 558 + jslib/common/src/abstractions/api.service.ts | 679 + .../common/src/abstractions/appId.service.ts | 4 + .../common/src/abstractions/audit.service.ts | 6 + jslib/common/src/abstractions/auth.service.ts | 25 + .../src/abstractions/broadcaster.service.ts | 5 + .../common/src/abstractions/cipher.service.ts | 79 + .../src/abstractions/collection.service.ts | 19 + .../common/src/abstractions/crypto.service.ts | 86 + .../abstractions/cryptoFunction.service.ts | 62 + .../src/abstractions/environment.service.ts | 34 + .../common/src/abstractions/event.service.ts | 7 + .../common/src/abstractions/export.service.ts | 11 + .../src/abstractions/fileUpload.service.ts | 18 + .../common/src/abstractions/folder.service.ts | 21 + jslib/common/src/abstractions/i18n.service.ts | 9 + .../common/src/abstractions/import.service.ts | 19 + .../src/abstractions/keyConnector.service.ts | 19 + jslib/common/src/abstractions/log.service.ts | 11 + .../src/abstractions/messaging.service.ts | 3 + .../src/abstractions/notifications.service.ts | 6 + .../src/abstractions/organization.service.ts | 11 + .../passwordGeneration.service.ts | 20 + .../abstractions/passwordReprompt.service.ts | 5 + .../src/abstractions/platformUtils.service.ts | 52 + .../common/src/abstractions/policy.service.ts | 32 + .../src/abstractions/provider.service.ts | 8 + .../common/src/abstractions/search.service.ts | 16 + jslib/common/src/abstractions/send.service.ts | 25 + .../src/abstractions/settings.service.ts | 6 + .../common/src/abstractions/state.service.ts | 307 + .../abstractions/stateMigration.service.ts | 4 + .../src/abstractions/storage.service.ts | 8 + jslib/common/src/abstractions/sync.service.ts | 19 + .../common/src/abstractions/system.service.ts | 6 + .../common/src/abstractions/token.service.ts | 32 + jslib/common/src/abstractions/totp.service.ts | 5 + .../src/abstractions/twoFactor.service.ts | 23 + .../abstractions/userVerification.service.ts | 12 + .../usernameGeneration.service.ts | 8 + .../src/abstractions/vaultTimeout.service.ts | 11 + .../common/src/enums/authenticationStatus.ts | 6 + jslib/common/src/enums/authenticationType.ts | 5 + jslib/common/src/enums/cipherRepromptType.ts | 4 + jslib/common/src/enums/cipherType.ts | 6 + jslib/common/src/enums/clientType.ts | 8 + jslib/common/src/enums/deviceType.ts | 23 + .../src/enums/emergencyAccessStatusType.ts | 7 + jslib/common/src/enums/emergencyAccessType.ts | 4 + jslib/common/src/enums/encryptionType.ts | 9 + jslib/common/src/enums/eventType.ts | 72 + jslib/common/src/enums/fieldType.ts | 6 + jslib/common/src/enums/fileUploadType.ts | 4 + jslib/common/src/enums/hashPurpose.ts | 4 + jslib/common/src/enums/htmlStorageLocation.ts | 5 + jslib/common/src/enums/importOptions.ts | 74 + jslib/common/src/enums/kdfType.ts | 7 + jslib/common/src/enums/keySuffixOptions.ts | 4 + jslib/common/src/enums/linkedIdType.ts | 40 + jslib/common/src/enums/logLevelType.ts | 6 + jslib/common/src/enums/notificationType.ts | 20 + .../src/enums/organizationUserStatusType.ts | 5 + .../common/src/enums/organizationUserType.ts | 7 + jslib/common/src/enums/paymentMethodType.ts | 11 + jslib/common/src/enums/permissions.ts | 27 + jslib/common/src/enums/planSponsorshipType.ts | 3 + jslib/common/src/enums/planType.ts | 14 + jslib/common/src/enums/policyType.ts | 13 + jslib/common/src/enums/productType.ts | 6 + .../src/enums/providerUserStatusType.ts | 5 + jslib/common/src/enums/providerUserType.ts | 4 + jslib/common/src/enums/secureNoteType.ts | 3 + jslib/common/src/enums/sendType.ts | 4 + jslib/common/src/enums/ssoEnums.ts | 33 + jslib/common/src/enums/stateVersion.ts | 7 + jslib/common/src/enums/storageLocation.ts | 5 + jslib/common/src/enums/themeType.ts | 7 + jslib/common/src/enums/transactionType.ts | 7 + .../common/src/enums/twoFactorProviderType.ts | 10 + jslib/common/src/enums/uriMatchType.ts | 8 + jslib/common/src/enums/verificationType.ts | 4 + jslib/common/src/factories/accountFactory.ts | 13 + .../src/factories/globalStateFactory.ts | 13 + jslib/common/src/factories/stateFactory.ts | 29 + jslib/common/src/globals.d.ts | 2 + .../src/importers/ascendoCsvImporter.ts | 59 + .../common/src/importers/avastCsvImporter.ts | 28 + .../common/src/importers/avastJsonImporter.ts | 68 + .../common/src/importers/aviraCsvImporter.ts | 41 + jslib/common/src/importers/baseImporter.ts | 466 + .../src/importers/bitwardenCsvImporter.ts | 120 + .../src/importers/bitwardenJsonImporter.ts | 179 + .../bitwardenPasswordProtectedImporter.ts | 81 + .../src/importers/blackBerryCsvImporter.ts | 36 + jslib/common/src/importers/blurCsvImporter.ts | 41 + .../src/importers/buttercupCsvImporter.ts | 50 + .../common/src/importers/chromeCsvImporter.ts | 28 + .../src/importers/clipperzHtmlImporter.ts | 88 + .../src/importers/codebookCsvImporter.ts | 47 + .../dashlaneImporters/dashlaneCsvImporter.ts | 271 + .../dashlaneImporters/dashlaneJsonImporter.ts | 171 + .../types/dashlaneCsvTypes.ts | 68 + .../src/importers/encryptrCsvImporter.ts | 60 + .../common/src/importers/enpassCsvImporter.ts | 133 + .../src/importers/enpassJsonImporter.ts | 191 + .../src/importers/firefoxCsvImporter.ts | 33 + .../src/importers/fsecureFskImporter.ts | 59 + .../common/src/importers/gnomeJsonImporter.ts | 71 + jslib/common/src/importers/importError.ts | 5 + jslib/common/src/importers/importer.ts | 6 + .../src/importers/kasperskyTxtImporter.ts | 124 + .../src/importers/keepass2XmlImporter.ts | 101 + .../src/importers/keepassxCsvImporter.ts | 44 + .../keeperImporters/keeperCsvImporter.ts | 45 + .../keeperImporters/keeperJsonImporter.ts | 69 + .../keeperImporters/types/keeperJsonTypes.ts | 41 + .../src/importers/lastpassCsvImporter.ts | 285 + .../src/importers/logMeOnceCsvImporter.ts | 31 + .../src/importers/meldiumCsvImporter.ts | 29 + .../src/importers/msecureCsvImporter.ts | 61 + jslib/common/src/importers/mykiCsvImporter.ts | 157 + .../src/importers/nordpassCsvImporter.ts | 127 + .../cipherImportContext.ts | 8 + .../onepassword1PifImporter.ts | 276 + .../onepassword1PuxImporter.ts | 637 + .../onepasswordCsvImporter.ts | 383 + .../onepasswordMacCsvImporter.ts | 31 + .../onepasswordWinCsvImporter.ts | 63 + .../types/onepassword1PuxImporterTypes.ts | 160 + .../src/importers/padlockCsvImporter.ts | 85 + .../src/importers/passkeepCsvImporter.ts | 39 + .../src/importers/passmanJsonImporter.ts | 61 + .../src/importers/passpackCsvImporter.ts | 103 + .../src/importers/passwordAgentCsvImporter.ts | 52 + .../src/importers/passwordBossJsonImporter.ts | 130 + .../importers/passwordDragonXmlImporter.ts | 63 + .../src/importers/passwordSafeXmlImporter.ts | 69 + .../importers/passwordWalletTxtImporter.ts | 47 + .../src/importers/rememBearCsvImporter.ts | 75 + .../src/importers/roboformCsvImporter.ts | 69 + .../common/src/importers/safariCsvImporter.ts | 30 + .../src/importers/safeInCloudXmlImporter.ts | 132 + .../src/importers/saferpassCsvImport.ts | 29 + .../src/importers/secureSafeCsvImporter.ts | 29 + .../src/importers/splashIdCsvImporter.ts | 57 + .../importers/stickyPasswordXmlImporter.ts | 83 + .../src/importers/truekeyCsvImporter.ts | 87 + jslib/common/src/importers/upmCsvImporter.ts | 32 + jslib/common/src/importers/yotiCsvImporter.ts | 28 + .../src/importers/zohoVaultCsvImporter.ts | 81 + jslib/common/src/misc/captcha_iframe.ts | 38 + jslib/common/src/misc/iframe_component.ts | 94 + .../src/misc/linkedFieldOption.decorator.ts | 27 + .../misc/logInStrategies/apiLogin.strategy.ts | 70 + .../misc/logInStrategies/logIn.strategy.ts | 170 + .../logInStrategies/passwordLogin.strategy.ts | 95 + .../misc/logInStrategies/ssoLogin.strategy.ts | 70 + jslib/common/src/misc/nodeUtils.ts | 34 + jslib/common/src/misc/sequentialize.ts | 57 + jslib/common/src/misc/serviceUtils.ts | 72 + jslib/common/src/misc/throttle.ts | 69 + jslib/common/src/misc/tldjs.noop.ts | 7 + jslib/common/src/misc/utils.ts | 406 + jslib/common/src/misc/webauthn_iframe.ts | 106 + jslib/common/src/misc/wordlist.ts | 7779 ++++ jslib/common/src/models/api/cardApi.ts | 23 + jslib/common/src/models/api/fieldApi.ts | 21 + jslib/common/src/models/api/identityApi.ts | 47 + jslib/common/src/models/api/loginApi.ts | 29 + jslib/common/src/models/api/loginUriApi.ts | 17 + jslib/common/src/models/api/permissionsApi.ts | 55 + jslib/common/src/models/api/secureNoteApi.ts | 14 + jslib/common/src/models/api/sendFileApi.ts | 19 + jslib/common/src/models/api/sendTextApi.ts | 15 + jslib/common/src/models/api/ssoConfigApi.ts | 136 + .../common/src/models/data/attachmentData.ts | 22 + jslib/common/src/models/data/cardData.ts | 23 + jslib/common/src/models/data/cipherData.ts | 85 + .../common/src/models/data/collectionData.ts | 17 + jslib/common/src/models/data/eventData.ts | 7 + jslib/common/src/models/data/fieldData.ts | 20 + jslib/common/src/models/data/folderData.ts | 15 + jslib/common/src/models/data/identityData.ts | 47 + jslib/common/src/models/data/loginData.ts | 28 + jslib/common/src/models/data/loginUriData.ts | 15 + .../src/models/data/organizationData.ts | 78 + .../src/models/data/passwordHistoryData.ts | 15 + jslib/common/src/models/data/policyData.ts | 18 + jslib/common/src/models/data/providerData.ts | 23 + .../common/src/models/data/secureNoteData.ts | 14 + jslib/common/src/models/data/sendData.ts | 58 + jslib/common/src/models/data/sendFileData.ts | 19 + jslib/common/src/models/data/sendTextData.ts | 15 + jslib/common/src/models/domain/account.ts | 175 + jslib/common/src/models/domain/attachment.ts | 87 + jslib/common/src/models/domain/authResult.ts | 17 + jslib/common/src/models/domain/card.ts | 65 + jslib/common/src/models/domain/cipher.ts | 238 + jslib/common/src/models/domain/collection.ts | 45 + .../src/models/domain/decryptParameters.ts | 8 + jslib/common/src/models/domain/domainBase.ts | 82 + .../src/models/domain/encArrayBuffer.ts | 3 + jslib/common/src/models/domain/encString.ts | 122 + .../src/models/domain/encryptedObject.ts | 8 + .../src/models/domain/environmentUrls.ts | 10 + jslib/common/src/models/domain/field.ts | 62 + jslib/common/src/models/domain/folder.ts | 40 + .../models/domain/generatedPasswordHistory.ts | 9 + jslib/common/src/models/domain/globalState.ts | 40 + jslib/common/src/models/domain/identity.ts | 113 + .../common/src/models/domain/importResult.ts | 14 + .../src/models/domain/logInCredentials.ts | 31 + jslib/common/src/models/domain/login.ts | 88 + jslib/common/src/models/domain/loginUri.ts | 54 + .../domain/masterPasswordPolicyOptions.ts | 10 + .../common/src/models/domain/organization.ts | 184 + jslib/common/src/models/domain/password.ts | 43 + .../domain/passwordGeneratorPolicyOptions.ts | 31 + jslib/common/src/models/domain/policy.ts | 25 + jslib/common/src/models/domain/provider.ts | 50 + .../domain/resetPasswordPolicyOptions.ts | 5 + jslib/common/src/models/domain/secureNote.ts | 29 + jslib/common/src/models/domain/send.ts | 114 + jslib/common/src/models/domain/sendAccess.ts | 77 + jslib/common/src/models/domain/sendFile.ts | 44 + jslib/common/src/models/domain/sendText.ts | 39 + .../src/models/domain/sortedCiphersCache.ts | 87 + jslib/common/src/models/domain/state.ts | 17 + .../src/models/domain/storageOptions.ts | 10 + .../src/models/domain/symmetricCryptoKey.ts | 57 + jslib/common/src/models/domain/treeNode.ts | 16 + jslib/common/src/models/domain/windowState.ts | 10 + jslib/common/src/models/export/card.ts | 65 + jslib/common/src/models/export/cipher.ts | 156 + .../common/src/models/export/cipherWithIds.ts | 16 + jslib/common/src/models/export/collection.ts | 46 + .../src/models/export/collectionWithId.ts | 14 + jslib/common/src/models/export/event.ts | 26 + jslib/common/src/models/export/field.ts | 52 + jslib/common/src/models/export/folder.ts | 32 + .../common/src/models/export/folderWithId.ts | 14 + jslib/common/src/models/export/identity.ts | 137 + jslib/common/src/models/export/login.ts | 65 + jslib/common/src/models/export/loginUri.ts | 41 + jslib/common/src/models/export/secureNote.ts | 31 + .../account/setKeyConnectorKeyRequest.ts | 24 + .../request/account/verifyOTPRequest.ts | 7 + .../src/models/request/attachmentRequest.ts | 6 + .../models/request/bitPayInvoiceRequest.ts | 9 + .../models/request/captchaProtectedRequest.ts | 3 + .../models/request/cipherBulkDeleteRequest.ts | 9 + .../models/request/cipherBulkMoveRequest.ts | 9 + .../request/cipherBulkRestoreRequest.ts | 7 + .../models/request/cipherBulkShareRequest.ts | 18 + .../request/cipherCollectionsRequest.ts | 7 + .../src/models/request/cipherCreateRequest.ts | 13 + .../src/models/request/cipherRequest.ts | 164 + .../src/models/request/cipherShareRequest.ts | 13 + .../src/models/request/cipherWithIdRequest.ts | 12 + .../src/models/request/collectionRequest.ts | 17 + .../models/request/deleteRecoverRequest.ts | 3 + .../src/models/request/deviceRequest.ts | 16 + .../src/models/request/deviceTokenRequest.ts | 7 + .../common/src/models/request/emailRequest.ts | 7 + .../src/models/request/emailTokenRequest.ts | 6 + .../request/emergencyAccessAcceptRequest.ts | 3 + .../request/emergencyAccessConfirmRequest.ts | 3 + .../request/emergencyAccessInviteRequest.ts | 7 + .../request/emergencyAccessPasswordRequest.ts | 4 + .../request/emergencyAccessUpdateRequest.ts | 7 + .../common/src/models/request/eventRequest.ts | 7 + .../src/models/request/folderRequest.ts | 9 + .../src/models/request/folderWithIdRequest.ts | 12 + .../common/src/models/request/groupRequest.ts | 8 + .../src/models/request/iapCheckRequest.ts | 5 + .../request/identityToken/apiTokenRequest.ts | 25 + .../identityToken/passwordTokenRequest.ts | 37 + .../request/identityToken/ssoTokenRequest.ts | 27 + .../request/identityToken/tokenRequest.ts | 43 + .../identityToken/tokenRequestTwoFactor.ts | 9 + .../models/request/importCiphersRequest.ts | 9 + .../models/request/importDirectoryRequest.ts | 9 + .../request/importDirectoryRequestGroup.ts | 5 + .../request/importDirectoryRequestUser.ts | 5 + .../importOrganizationCiphersRequest.ts | 9 + jslib/common/src/models/request/kdfRequest.ts | 8 + .../request/keyConnectorUserKeyRequest.ts | 7 + .../common/src/models/request/keysRequest.ts | 9 + jslib/common/src/models/request/kvpRequest.ts | 9 + .../organizationSponsorshipCreateRequest.ts | 7 + .../organizationSponsorshipRedeemRequest.ts | 6 + .../organization/organizationSsoRequest.ts | 6 + .../request/organizationCreateRequest.ts | 27 + .../request/organizationImportGroupRequest.ts | 18 + .../organizationImportMemberRequest.ts | 13 + .../request/organizationImportRequest.ts | 31 + .../models/request/organizationKeysRequest.ts | 7 + .../organizationSubscriptionUpdateRequest.ts | 3 + .../organizationTaxInfoUpdateRequest.ts | 9 + .../request/organizationUpdateRequest.ts | 9 + .../request/organizationUpgradeRequest.ts | 14 + .../request/organizationUserAcceptRequest.ts | 3 + .../organizationUserBulkConfirmRequest.ts | 12 + .../request/organizationUserBulkRequest.ts | 7 + .../request/organizationUserConfirmRequest.ts | 3 + .../request/organizationUserInviteRequest.ts | 12 + ...ationUserResetPasswordEnrollmentRequest.ts | 3 + .../organizationUserResetPasswordRequest.ts | 4 + .../organizationUserUpdateGroupsRequest.ts | 3 + .../request/organizationUserUpdateRequest.ts | 11 + .../src/models/request/passwordHintRequest.ts | 7 + .../models/request/passwordHistoryRequest.ts | 4 + .../src/models/request/passwordRequest.ts | 6 + .../src/models/request/paymentRequest.ts | 7 + .../src/models/request/policyRequest.ts | 7 + .../src/models/request/preloginRequest.ts | 7 + .../providerAddOrganizationRequest.ts | 4 + .../providerOrganizationCreateRequest.ts | 8 + .../request/provider/providerSetupRequest.ts | 7 + .../request/provider/providerUpdateRequest.ts | 5 + .../provider/providerUserAcceptRequest.ts | 3 + .../providerUserBulkConfirmRequest.ts | 12 + .../provider/providerUserBulkRequest.ts | 7 + .../provider/providerUserConfirmRequest.ts | 3 + .../provider/providerUserInviteRequest.ts | 6 + .../provider/providerUserUpdateRequest.ts | 5 + .../models/request/referenceEventRequest.ts | 5 + .../src/models/request/registerRequest.ts | 26 + .../common/src/models/request/seatRequest.ts | 3 + .../request/secretVerificationRequest.ts | 4 + .../request/selectionReadOnlyRequest.ts | 11 + .../src/models/request/sendAccessRequest.ts | 3 + .../common/src/models/request/sendRequest.ts | 48 + .../src/models/request/sendWithIdRequest.ts | 12 + .../src/models/request/setPasswordRequest.ts | 31 + .../src/models/request/storageRequest.ts | 3 + .../models/request/taxInfoUpdateRequest.ts | 4 + .../models/request/twoFactorEmailRequest.ts | 5 + .../request/twoFactorProviderRequest.ts | 7 + .../request/twoFactorRecoveryRequest.ts | 6 + .../models/request/updateDomainsRequest.ts | 4 + .../src/models/request/updateKeyRequest.ts | 12 + .../models/request/updateProfileRequest.ts | 10 + .../request/updateTempPasswordRequest.ts | 5 + .../updateTwoFactorAuthenticatorRequest.ts | 6 + .../request/updateTwoFactorDuoRequest.ts | 7 + .../request/updateTwoFactorEmailRequest.ts | 6 + .../updateTwoFactorWebAuthnDeleteRequest.ts | 5 + .../request/updateTwoFactorWebAuthnRequest.ts | 7 + .../request/updateTwoFactorYubioOtpRequest.ts | 10 + .../src/models/request/verifyBankRequest.ts | 4 + .../request/verifyDeleteRecoverRequest.ts | 9 + .../src/models/request/verifyEmailRequest.ts | 9 + .../src/models/response/apiKeyResponse.ts | 10 + .../src/models/response/attachmentResponse.ts | 20 + .../response/attachmentUploadDataResponse.ts | 23 + .../src/models/response/baseResponse.ts | 43 + .../src/models/response/billingResponse.ts | 83 + .../models/response/breachAccountResponse.ts | 32 + .../src/models/response/cipherResponse.ts | 92 + .../src/models/response/collectionResponse.ts | 38 + .../src/models/response/deviceResponse.ts | 20 + .../src/models/response/domainsResponse.ts | 20 + .../response/emergencyAccessResponse.ts | 82 + .../src/models/response/errorResponse.ts | 74 + .../src/models/response/eventResponse.ts | 41 + .../src/models/response/folderResponse.ts | 14 + .../models/response/globalDomainResponse.ts | 14 + .../src/models/response/groupResponse.ts | 31 + .../response/identityCaptchaResponse.ts | 10 + .../models/response/identityTokenResponse.ts | 38 + .../response/identityTwoFactorResponse.ts | 24 + .../response/keyConnectorUserKeyResponse.ts | 10 + .../src/models/response/keysResponse.ts | 12 + .../src/models/response/listResponse.ts | 13 + .../models/response/notificationResponse.ts | 98 + .../organization/organizationSsoResponse.ts | 35 + .../organizationAutoEnrollStatusResponse.ts | 12 + .../response/organizationKeysResponse.ts | 7 + .../models/response/organizationResponse.ts | 60 + .../organizationSubscriptionResponse.ts | 27 + .../organizationUserBulkPublicKeyResponse.ts | 14 + .../response/organizationUserBulkResponse.ts | 12 + .../response/organizationUserResponse.ts | 70 + .../response/passwordHistoryResponse.ts | 12 + .../src/models/response/paymentResponse.ts | 18 + .../src/models/response/planResponse.ts | 95 + .../src/models/response/policyResponse.ts | 20 + .../src/models/response/preloginResponse.ts | 14 + .../response/profileOrganizationResponse.ts | 81 + .../profileProviderOrganizationResponse.ts | 8 + .../response/profileProviderResponse.ts | 30 + .../src/models/response/profileResponse.ts | 55 + .../provider/providerOrganizationResponse.ts | 31 + .../response/provider/providerResponse.ts | 16 + .../providerUserBulkPublicKeyResponse.ts | 3 + .../provider/providerUserBulkResponse.ts | 12 + .../response/provider/providerUserResponse.ts | 32 + .../response/selectionReadOnlyResponse.ts | 14 + .../src/models/response/sendAccessResponse.ts | 35 + .../response/sendFileDownloadDataResponse.ts | 11 + .../response/sendFileUploadDataResponse.ts | 17 + .../src/models/response/sendResponse.ts | 52 + .../models/response/subscriptionResponse.ts | 85 + .../src/models/response/syncResponse.ts | 57 + .../src/models/response/taxInfoResponse.ts | 24 + .../src/models/response/taxRateResponse.ts | 18 + .../twoFactorAuthenticatorResponse.ts | 12 + .../models/response/twoFactorDuoResponse.ts | 16 + .../models/response/twoFactorEmailResponse.ts | 12 + .../response/twoFactorProviderResponse.ts | 14 + .../response/twoFactorRescoverResponse.ts | 10 + .../response/twoFactorWebAuthnResponse.ts | 60 + .../response/twoFactorYubiKeyResponse.ts | 22 + .../src/models/response/userKeyResponse.ts | 12 + .../common/src/models/view/attachmentView.ts | 35 + jslib/common/src/models/view/cardView.ts | 82 + jslib/common/src/models/view/cipherView.ts | 134 + .../common/src/models/view/collectionView.ts | 28 + jslib/common/src/models/view/eventView.ts | 27 + jslib/common/src/models/view/fieldView.ts | 28 + jslib/common/src/models/view/folderView.ts | 19 + jslib/common/src/models/view/identityView.ts | 142 + jslib/common/src/models/view/itemView.ts | 8 + jslib/common/src/models/view/loginUriView.ts | 127 + jslib/common/src/models/view/loginView.ts | 63 + .../src/models/view/passwordHistoryView.ts | 16 + .../common/src/models/view/secureNoteView.ts | 21 + .../common/src/models/view/sendAccessView.ts | 27 + jslib/common/src/models/view/sendFileView.ts | 31 + jslib/common/src/models/view/sendTextView.ts | 20 + jslib/common/src/models/view/sendView.ts | 68 + jslib/common/src/models/view/ssoConfigView.ts | 104 + jslib/common/src/models/view/view.ts | 1 + jslib/common/src/services/api.service.ts | 2534 ++ jslib/common/src/services/appId.service.ts | 31 + jslib/common/src/services/audit.service.ts | 44 + jslib/common/src/services/auth.service.ts | 198 + .../src/services/azureFileUpload.service.ts | 214 + .../services/bitwardenFileUpload.service.ts | 34 + .../src/services/broadcaster.service.ts | 28 + jslib/common/src/services/cipher.service.ts | 1282 + .../common/src/services/collection.service.ts | 157 + .../common/src/services/consoleLog.service.ts | 72 + .../common/src/services/container.service.ts | 20 + jslib/common/src/services/crypto.service.ts | 969 + .../src/services/environment.service.ts | 188 + jslib/common/src/services/event.service.ts | 99 + jslib/common/src/services/export.service.ts | 444 + .../common/src/services/fileUpload.service.ts | 108 + jslib/common/src/services/folder.service.ts | 194 + jslib/common/src/services/i18n.service.ts | 175 + jslib/common/src/services/import.service.ts | 382 + .../src/services/keyConnector.service.ts | 134 + .../src/services/noopMessaging.service.ts | 7 + .../src/services/notifications.service.ts | 231 + .../src/services/organization.service.ts | 55 + .../services/passwordGeneration.service.ts | 572 + jslib/common/src/services/policy.service.ts | 247 + jslib/common/src/services/provider.service.ts | 34 + jslib/common/src/services/search.service.ts | 284 + jslib/common/src/services/send.service.ts | 297 + jslib/common/src/services/settings.service.ts | 56 + jslib/common/src/services/state.service.ts | 2536 ++ .../src/services/stateMigration.service.ts | 513 + jslib/common/src/services/sync.service.ts | 400 + jslib/common/src/services/system.service.ts | 90 + jslib/common/src/services/token.service.ts | 195 + jslib/common/src/services/totp.service.ts | 174 + .../common/src/services/twoFactor.service.ts | 186 + .../src/services/userVerification.service.ts | 88 + .../services/usernameGeneration.service.ts | 128 + .../src/services/vaultTimeout.service.ts | 222 + .../src/services/webCryptoFunction.service.ts | 356 + jslib/common/src/types/verification.ts | 6 + jslib/common/tsconfig.json | 10 + jslib/common/tsconfig.spec.json | 3 + jslib/electron/jest.config.js | 16 + jslib/electron/package-lock.json | 2393 + jslib/electron/package.json | 32 + .../spec/services/electronLog.service.spec.ts | 9 + jslib/electron/spec/test.ts | 0 jslib/electron/spec/utils.spec.ts | 27 + jslib/electron/src/baseMenu.ts | 225 + jslib/electron/src/globals.d.ts | 0 .../src/services/electronCrypto.service.ts | 71 + .../src/services/electronLog.service.ts | 45 + .../services/electronMainMessaging.service.ts | 65 + .../services/electronPlatformUtils.service.ts | 217 + .../electronRendererMessaging.service.ts | 26 + .../electronRendererSecureStorage.service.ts | 43 + .../electronRendererStorage.service.ts | 34 + .../src/services/electronStorage.service.ts | 60 + jslib/electron/src/tray.main.ts | 186 + jslib/electron/src/updater.main.ts | 156 + jslib/electron/src/utils.ts | 76 + jslib/electron/src/window.main.ts | 293 + jslib/electron/tsconfig.json | 11 + jslib/electron/tsconfig.spec.json | 3 + jslib/jest.config.js | 18 + jslib/node/jest.config.js | 15 + jslib/node/package-lock.json | 1183 + jslib/node/package.json | 38 + .../node/spec/cli/consoleLog.service.spec.ts | 45 + .../nodeCryptoFunction.service.spec.ts | 518 + jslib/node/spec/test.ts | 0 jslib/node/src/cli/baseProgram.ts | 113 + jslib/node/src/cli/commands/login.command.ts | 624 + jslib/node/src/cli/commands/logout.command.ts | 22 + jslib/node/src/cli/commands/update.command.ts | 104 + jslib/node/src/cli/models/response.ts | 50 + .../src/cli/models/response/baseResponse.ts | 3 + .../src/cli/models/response/fileResponse.ts | 13 + .../src/cli/models/response/listResponse.ts | 11 + .../cli/models/response/messageResponse.ts | 15 + .../src/cli/models/response/stringResponse.ts | 11 + .../cli/services/cliPlatformUtils.service.ts | 166 + .../src/cli/services/consoleLog.service.ts | 22 + jslib/node/src/globals.d.ts | 0 .../node/src/services/lowdbStorage.service.ts | 148 + jslib/node/src/services/nodeApi.service.ts | 43 + .../services/nodeCryptoFunction.service.ts | 301 + jslib/node/tsconfig.json | 11 + jslib/node/tsconfig.spec.json | 3 + jslib/package-lock.json | 36691 ++++++++++++++++ jslib/package.json | 74 + jslib/shared/eslintrc.json | 29 + jslib/shared/tsconfig.json | 15 + jslib/tsconfig.json | 30 + jslib/tsconfig.spec.json | 3 + src/app/app.module.ts | 37 +- src/app/services/services.module.ts | 2 +- 694 files changed, 104863 insertions(+), 14 deletions(-) delete mode 100644 .gitmodules delete mode 160000 jslib create mode 100644 jslib/.gitignore create mode 100644 jslib/.prettierrc.json create mode 100644 jslib/angular/jest.config.js create mode 100644 jslib/angular/package-lock.json create mode 100644 jslib/angular/package.json create mode 100644 jslib/angular/spec/test.ts create mode 100644 jslib/angular/src/components/callout.component.html create mode 100644 jslib/angular/src/components/callout.component.ts create mode 100644 jslib/angular/src/components/environment.component.ts create mode 100644 jslib/angular/src/components/icon.component.html create mode 100644 jslib/angular/src/components/icon.component.ts create mode 100644 jslib/angular/src/components/modal/dynamic-modal.component.ts create mode 100644 jslib/angular/src/components/modal/modal-injector.ts create mode 100644 jslib/angular/src/components/modal/modal.ref.ts create mode 100644 jslib/angular/src/components/password-reprompt.component.ts create mode 100644 jslib/angular/src/components/toastr.component.ts create mode 100644 jslib/angular/src/components/user-verification.component.html create mode 100644 jslib/angular/src/components/user-verification.component.ts create mode 100644 jslib/angular/src/directives/a11y-title.directive.ts create mode 100644 jslib/angular/src/directives/api-action.directive.ts create mode 100644 jslib/angular/src/directives/autofocus.directive.ts create mode 100644 jslib/angular/src/directives/blur-click.directive.ts create mode 100644 jslib/angular/src/directives/box-row.directive.ts create mode 100644 jslib/angular/src/directives/fallback-src.directive.ts create mode 100644 jslib/angular/src/directives/stop-click.directive.ts create mode 100644 jslib/angular/src/directives/stop-prop.directive.ts create mode 100644 jslib/angular/src/pipes/i18n.pipe.ts create mode 100644 jslib/angular/src/pipes/search-ciphers.pipe.ts create mode 100644 jslib/angular/src/scss/bwicons/fonts/bwi-font.svg create mode 100644 jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf create mode 100644 jslib/angular/src/scss/bwicons/fonts/bwi-font.woff create mode 100644 jslib/angular/src/scss/bwicons/fonts/bwi-font.woff2 create mode 100644 jslib/angular/src/scss/bwicons/styles/style.scss create mode 100644 jslib/angular/src/scss/icons.scss create mode 100644 jslib/angular/src/scss/webfonts.css create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-italic-600.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-normal-600.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff create mode 100644 jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff create mode 100644 jslib/angular/src/services/auth-guard.service.ts create mode 100644 jslib/angular/src/services/broadcaster.service.ts create mode 100644 jslib/angular/src/services/jslib-services.module.ts create mode 100644 jslib/angular/src/services/lock-guard.service.ts create mode 100644 jslib/angular/src/services/modal.service.ts create mode 100644 jslib/angular/src/services/passwordReprompt.service.ts create mode 100644 jslib/angular/src/services/unauth-guard.service.ts create mode 100644 jslib/angular/src/services/validation.service.ts create mode 100644 jslib/angular/tsconfig.json create mode 100644 jslib/angular/tsconfig.spec.json create mode 100644 jslib/common/jest.config.js create mode 100644 jslib/common/package-lock.json create mode 100644 jslib/common/package.json create mode 100644 jslib/common/spec/domain/attachment.spec.ts create mode 100644 jslib/common/spec/domain/card.spec.ts create mode 100644 jslib/common/spec/domain/cipher.spec.ts create mode 100644 jslib/common/spec/domain/collection.spec.ts create mode 100644 jslib/common/spec/domain/encString.spec.ts create mode 100644 jslib/common/spec/domain/field.spec.ts create mode 100644 jslib/common/spec/domain/folder.spec.ts create mode 100644 jslib/common/spec/domain/identity.spec.ts create mode 100644 jslib/common/spec/domain/login.spec.ts create mode 100644 jslib/common/spec/domain/loginUri.spec.ts create mode 100644 jslib/common/spec/domain/password.spec.ts create mode 100644 jslib/common/spec/domain/secureNote.spec.ts create mode 100644 jslib/common/spec/domain/send.spec.ts create mode 100644 jslib/common/spec/domain/sendAccess.spec.ts create mode 100644 jslib/common/spec/domain/sendFile.spec.ts create mode 100644 jslib/common/spec/domain/sendText.spec.ts create mode 100644 jslib/common/spec/domain/symmetricCryptoKey.spec.ts create mode 100644 jslib/common/spec/importers/bitwardenJsonImporter.spec.ts create mode 100644 jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts create mode 100644 jslib/common/spec/importers/dashlaneCsvImporter.spec.ts create mode 100644 jslib/common/spec/importers/firefoxCsvImporter.spec.ts create mode 100644 jslib/common/spec/importers/fsecureFskImporter.spec.ts create mode 100644 jslib/common/spec/importers/keepass2XmlImporter.spec.ts create mode 100644 jslib/common/spec/importers/keeperJsonImporter.spec.ts create mode 100644 jslib/common/spec/importers/lastpassCsvImporter.spec.ts create mode 100644 jslib/common/spec/importers/mykiCsvImporter.spec.ts create mode 100644 jslib/common/spec/importers/nordpassCsvImporter.spec.ts create mode 100644 jslib/common/spec/importers/onepassword1PifImporter.spec.ts create mode 100644 jslib/common/spec/importers/onepassword1PuxImporter.spec.ts create mode 100644 jslib/common/spec/importers/onepasswordMacCsvImporter.spec.ts create mode 100644 jslib/common/spec/importers/onepasswordWinCsvImporter.spec.ts create mode 100644 jslib/common/spec/importers/safariCsvImporter.spec.ts create mode 100644 jslib/common/spec/importers/testData/bitwardenJson/empty.json.ts create mode 100644 jslib/common/spec/importers/testData/bitwardenJson/passwordProtected.json.ts create mode 100644 jslib/common/spec/importers/testData/dashlaneCsv/credentials.csv.ts create mode 100644 jslib/common/spec/importers/testData/dashlaneCsv/id.csv.ts create mode 100644 jslib/common/spec/importers/testData/dashlaneCsv/multiplePersonalInfo.csv.ts create mode 100644 jslib/common/spec/importers/testData/dashlaneCsv/payments.csv.ts create mode 100644 jslib/common/spec/importers/testData/dashlaneCsv/personalInfo.csv.ts create mode 100644 jslib/common/spec/importers/testData/dashlaneCsv/securenotes.csv.ts create mode 100644 jslib/common/spec/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts create mode 100644 jslib/common/spec/importers/testData/firefoxCsv/simplePasswordData.csv.ts create mode 100644 jslib/common/spec/importers/testData/keeperJson/testData.ts create mode 100644 jslib/common/spec/importers/testData/mykiCsv/UserAccount.csv.ts create mode 100644 jslib/common/spec/importers/testData/mykiCsv/UserCreditCard.csv.ts create mode 100644 jslib/common/spec/importers/testData/mykiCsv/UserIdCard.csv.ts create mode 100644 jslib/common/spec/importers/testData/mykiCsv/UserIdentity.csv.ts create mode 100644 jslib/common/spec/importers/testData/mykiCsv/UserNote.csv.ts create mode 100644 jslib/common/spec/importers/testData/mykiCsv/UserTwofa.csv.ts create mode 100644 jslib/common/spec/importers/testData/nordpassCsv/nordpass.card.csv.ts create mode 100644 jslib/common/spec/importers/testData/nordpassCsv/nordpass.identity.csv.ts create mode 100644 jslib/common/spec/importers/testData/nordpassCsv/nordpass.login.csv.ts create mode 100644 jslib/common/spec/importers/testData/nordpassCsv/nordpass.secureNote.csv.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/APICredentials.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/BankAccount.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/CreditCard.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/Database.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/DriversLicense.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/EmailAccount.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/Emailfield.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/EmailfieldOnIdentity.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/EmailfieldOnIdentity_Prefilled.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/IdentityData.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/LoginData.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/MedicalRecord.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/Membership.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/Onepux_example.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/OutdoorLicense.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/Passport.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/Password.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/RewardsProgram.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/SSN.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/SanitizedExport.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/SecureNote.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/Server.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/SoftwareLicense.ts create mode 100644 jslib/common/spec/importers/testData/onePassword1Pux/WirelessRouter.ts create mode 100644 jslib/common/spec/importers/testData/onePasswordCsv/creditCard.mac.csv.ts create mode 100644 jslib/common/spec/importers/testData/onePasswordCsv/creditCard.windows.csv.ts create mode 100644 jslib/common/spec/importers/testData/onePasswordCsv/identity.mac.csv.ts create mode 100644 jslib/common/spec/importers/testData/onePasswordCsv/identity.windows.csv.ts create mode 100644 jslib/common/spec/importers/testData/onePasswordCsv/multipleItems.mac.csv.ts create mode 100644 jslib/common/spec/importers/testData/onePasswordCsv/multipleItems.windows.csv.ts create mode 100644 jslib/common/spec/importers/testData/safariCsv/oldSimplePasswordData.csv.ts create mode 100644 jslib/common/spec/importers/testData/safariCsv/simplePasswordData.csv.ts create mode 100644 jslib/common/spec/importers/testData/safeInCloud/testData.xml.ts create mode 100644 jslib/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts create mode 100644 jslib/common/spec/misc/logInStrategies/logIn.strategy.spec.ts create mode 100644 jslib/common/spec/misc/logInStrategies/passwordLogIn.strategy.spec.ts create mode 100644 jslib/common/spec/misc/logInStrategies/ssoLogIn.strategy.spec.ts create mode 100644 jslib/common/spec/misc/sequentialize.spec.ts create mode 100644 jslib/common/spec/misc/throttle.spec.ts create mode 100644 jslib/common/spec/misc/utils.spec.ts create mode 100644 jslib/common/spec/services/cipher.service.spec.ts create mode 100644 jslib/common/spec/services/consoleLog.service.spec.ts create mode 100644 jslib/common/spec/services/export.service.spec.ts create mode 100644 jslib/common/spec/services/import.service.spec.ts create mode 100644 jslib/common/spec/services/stateMigration.service.ts create mode 100644 jslib/common/spec/test.ts create mode 100644 jslib/common/spec/utils.ts create mode 100644 jslib/common/spec/web/services/webCryptoFunction.service.spec.ts create mode 100644 jslib/common/src/abstractions/api.service.ts create mode 100644 jslib/common/src/abstractions/appId.service.ts create mode 100644 jslib/common/src/abstractions/audit.service.ts create mode 100644 jslib/common/src/abstractions/auth.service.ts create mode 100644 jslib/common/src/abstractions/broadcaster.service.ts create mode 100644 jslib/common/src/abstractions/cipher.service.ts create mode 100644 jslib/common/src/abstractions/collection.service.ts create mode 100644 jslib/common/src/abstractions/crypto.service.ts create mode 100644 jslib/common/src/abstractions/cryptoFunction.service.ts create mode 100644 jslib/common/src/abstractions/environment.service.ts create mode 100644 jslib/common/src/abstractions/event.service.ts create mode 100644 jslib/common/src/abstractions/export.service.ts create mode 100644 jslib/common/src/abstractions/fileUpload.service.ts create mode 100644 jslib/common/src/abstractions/folder.service.ts create mode 100644 jslib/common/src/abstractions/i18n.service.ts create mode 100644 jslib/common/src/abstractions/import.service.ts create mode 100644 jslib/common/src/abstractions/keyConnector.service.ts create mode 100644 jslib/common/src/abstractions/log.service.ts create mode 100644 jslib/common/src/abstractions/messaging.service.ts create mode 100644 jslib/common/src/abstractions/notifications.service.ts create mode 100644 jslib/common/src/abstractions/organization.service.ts create mode 100644 jslib/common/src/abstractions/passwordGeneration.service.ts create mode 100644 jslib/common/src/abstractions/passwordReprompt.service.ts create mode 100644 jslib/common/src/abstractions/platformUtils.service.ts create mode 100644 jslib/common/src/abstractions/policy.service.ts create mode 100644 jslib/common/src/abstractions/provider.service.ts create mode 100644 jslib/common/src/abstractions/search.service.ts create mode 100644 jslib/common/src/abstractions/send.service.ts create mode 100644 jslib/common/src/abstractions/settings.service.ts create mode 100644 jslib/common/src/abstractions/state.service.ts create mode 100644 jslib/common/src/abstractions/stateMigration.service.ts create mode 100644 jslib/common/src/abstractions/storage.service.ts create mode 100644 jslib/common/src/abstractions/sync.service.ts create mode 100644 jslib/common/src/abstractions/system.service.ts create mode 100644 jslib/common/src/abstractions/token.service.ts create mode 100644 jslib/common/src/abstractions/totp.service.ts create mode 100644 jslib/common/src/abstractions/twoFactor.service.ts create mode 100644 jslib/common/src/abstractions/userVerification.service.ts create mode 100644 jslib/common/src/abstractions/usernameGeneration.service.ts create mode 100644 jslib/common/src/abstractions/vaultTimeout.service.ts create mode 100644 jslib/common/src/enums/authenticationStatus.ts create mode 100644 jslib/common/src/enums/authenticationType.ts create mode 100644 jslib/common/src/enums/cipherRepromptType.ts create mode 100644 jslib/common/src/enums/cipherType.ts create mode 100644 jslib/common/src/enums/clientType.ts create mode 100644 jslib/common/src/enums/deviceType.ts create mode 100644 jslib/common/src/enums/emergencyAccessStatusType.ts create mode 100644 jslib/common/src/enums/emergencyAccessType.ts create mode 100644 jslib/common/src/enums/encryptionType.ts create mode 100644 jslib/common/src/enums/eventType.ts create mode 100644 jslib/common/src/enums/fieldType.ts create mode 100644 jslib/common/src/enums/fileUploadType.ts create mode 100644 jslib/common/src/enums/hashPurpose.ts create mode 100644 jslib/common/src/enums/htmlStorageLocation.ts create mode 100644 jslib/common/src/enums/importOptions.ts create mode 100644 jslib/common/src/enums/kdfType.ts create mode 100644 jslib/common/src/enums/keySuffixOptions.ts create mode 100644 jslib/common/src/enums/linkedIdType.ts create mode 100644 jslib/common/src/enums/logLevelType.ts create mode 100644 jslib/common/src/enums/notificationType.ts create mode 100644 jslib/common/src/enums/organizationUserStatusType.ts create mode 100644 jslib/common/src/enums/organizationUserType.ts create mode 100644 jslib/common/src/enums/paymentMethodType.ts create mode 100644 jslib/common/src/enums/permissions.ts create mode 100644 jslib/common/src/enums/planSponsorshipType.ts create mode 100644 jslib/common/src/enums/planType.ts create mode 100644 jslib/common/src/enums/policyType.ts create mode 100644 jslib/common/src/enums/productType.ts create mode 100644 jslib/common/src/enums/providerUserStatusType.ts create mode 100644 jslib/common/src/enums/providerUserType.ts create mode 100644 jslib/common/src/enums/secureNoteType.ts create mode 100644 jslib/common/src/enums/sendType.ts create mode 100644 jslib/common/src/enums/ssoEnums.ts create mode 100644 jslib/common/src/enums/stateVersion.ts create mode 100644 jslib/common/src/enums/storageLocation.ts create mode 100644 jslib/common/src/enums/themeType.ts create mode 100644 jslib/common/src/enums/transactionType.ts create mode 100644 jslib/common/src/enums/twoFactorProviderType.ts create mode 100644 jslib/common/src/enums/uriMatchType.ts create mode 100644 jslib/common/src/enums/verificationType.ts create mode 100644 jslib/common/src/factories/accountFactory.ts create mode 100644 jslib/common/src/factories/globalStateFactory.ts create mode 100644 jslib/common/src/factories/stateFactory.ts create mode 100644 jslib/common/src/globals.d.ts create mode 100644 jslib/common/src/importers/ascendoCsvImporter.ts create mode 100644 jslib/common/src/importers/avastCsvImporter.ts create mode 100644 jslib/common/src/importers/avastJsonImporter.ts create mode 100644 jslib/common/src/importers/aviraCsvImporter.ts create mode 100644 jslib/common/src/importers/baseImporter.ts create mode 100644 jslib/common/src/importers/bitwardenCsvImporter.ts create mode 100644 jslib/common/src/importers/bitwardenJsonImporter.ts create mode 100644 jslib/common/src/importers/bitwardenPasswordProtectedImporter.ts create mode 100644 jslib/common/src/importers/blackBerryCsvImporter.ts create mode 100644 jslib/common/src/importers/blurCsvImporter.ts create mode 100644 jslib/common/src/importers/buttercupCsvImporter.ts create mode 100644 jslib/common/src/importers/chromeCsvImporter.ts create mode 100644 jslib/common/src/importers/clipperzHtmlImporter.ts create mode 100644 jslib/common/src/importers/codebookCsvImporter.ts create mode 100644 jslib/common/src/importers/dashlaneImporters/dashlaneCsvImporter.ts create mode 100644 jslib/common/src/importers/dashlaneImporters/dashlaneJsonImporter.ts create mode 100644 jslib/common/src/importers/dashlaneImporters/types/dashlaneCsvTypes.ts create mode 100644 jslib/common/src/importers/encryptrCsvImporter.ts create mode 100644 jslib/common/src/importers/enpassCsvImporter.ts create mode 100644 jslib/common/src/importers/enpassJsonImporter.ts create mode 100644 jslib/common/src/importers/firefoxCsvImporter.ts create mode 100644 jslib/common/src/importers/fsecureFskImporter.ts create mode 100644 jslib/common/src/importers/gnomeJsonImporter.ts create mode 100644 jslib/common/src/importers/importError.ts create mode 100644 jslib/common/src/importers/importer.ts create mode 100644 jslib/common/src/importers/kasperskyTxtImporter.ts create mode 100644 jslib/common/src/importers/keepass2XmlImporter.ts create mode 100644 jslib/common/src/importers/keepassxCsvImporter.ts create mode 100644 jslib/common/src/importers/keeperImporters/keeperCsvImporter.ts create mode 100644 jslib/common/src/importers/keeperImporters/keeperJsonImporter.ts create mode 100644 jslib/common/src/importers/keeperImporters/types/keeperJsonTypes.ts create mode 100644 jslib/common/src/importers/lastpassCsvImporter.ts create mode 100644 jslib/common/src/importers/logMeOnceCsvImporter.ts create mode 100644 jslib/common/src/importers/meldiumCsvImporter.ts create mode 100644 jslib/common/src/importers/msecureCsvImporter.ts create mode 100644 jslib/common/src/importers/mykiCsvImporter.ts create mode 100644 jslib/common/src/importers/nordpassCsvImporter.ts create mode 100644 jslib/common/src/importers/onepasswordImporters/cipherImportContext.ts create mode 100644 jslib/common/src/importers/onepasswordImporters/onepassword1PifImporter.ts create mode 100644 jslib/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts create mode 100644 jslib/common/src/importers/onepasswordImporters/onepasswordCsvImporter.ts create mode 100644 jslib/common/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts create mode 100644 jslib/common/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts create mode 100644 jslib/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts create mode 100644 jslib/common/src/importers/padlockCsvImporter.ts create mode 100644 jslib/common/src/importers/passkeepCsvImporter.ts create mode 100644 jslib/common/src/importers/passmanJsonImporter.ts create mode 100644 jslib/common/src/importers/passpackCsvImporter.ts create mode 100644 jslib/common/src/importers/passwordAgentCsvImporter.ts create mode 100644 jslib/common/src/importers/passwordBossJsonImporter.ts create mode 100644 jslib/common/src/importers/passwordDragonXmlImporter.ts create mode 100644 jslib/common/src/importers/passwordSafeXmlImporter.ts create mode 100644 jslib/common/src/importers/passwordWalletTxtImporter.ts create mode 100644 jslib/common/src/importers/rememBearCsvImporter.ts create mode 100644 jslib/common/src/importers/roboformCsvImporter.ts create mode 100644 jslib/common/src/importers/safariCsvImporter.ts create mode 100644 jslib/common/src/importers/safeInCloudXmlImporter.ts create mode 100644 jslib/common/src/importers/saferpassCsvImport.ts create mode 100644 jslib/common/src/importers/secureSafeCsvImporter.ts create mode 100644 jslib/common/src/importers/splashIdCsvImporter.ts create mode 100644 jslib/common/src/importers/stickyPasswordXmlImporter.ts create mode 100644 jslib/common/src/importers/truekeyCsvImporter.ts create mode 100644 jslib/common/src/importers/upmCsvImporter.ts create mode 100644 jslib/common/src/importers/yotiCsvImporter.ts create mode 100644 jslib/common/src/importers/zohoVaultCsvImporter.ts create mode 100644 jslib/common/src/misc/captcha_iframe.ts create mode 100644 jslib/common/src/misc/iframe_component.ts create mode 100644 jslib/common/src/misc/linkedFieldOption.decorator.ts create mode 100644 jslib/common/src/misc/logInStrategies/apiLogin.strategy.ts create mode 100644 jslib/common/src/misc/logInStrategies/logIn.strategy.ts create mode 100644 jslib/common/src/misc/logInStrategies/passwordLogin.strategy.ts create mode 100644 jslib/common/src/misc/logInStrategies/ssoLogin.strategy.ts create mode 100644 jslib/common/src/misc/nodeUtils.ts create mode 100644 jslib/common/src/misc/sequentialize.ts create mode 100644 jslib/common/src/misc/serviceUtils.ts create mode 100644 jslib/common/src/misc/throttle.ts create mode 100644 jslib/common/src/misc/tldjs.noop.ts create mode 100644 jslib/common/src/misc/utils.ts create mode 100644 jslib/common/src/misc/webauthn_iframe.ts create mode 100644 jslib/common/src/misc/wordlist.ts create mode 100644 jslib/common/src/models/api/cardApi.ts create mode 100644 jslib/common/src/models/api/fieldApi.ts create mode 100644 jslib/common/src/models/api/identityApi.ts create mode 100644 jslib/common/src/models/api/loginApi.ts create mode 100644 jslib/common/src/models/api/loginUriApi.ts create mode 100644 jslib/common/src/models/api/permissionsApi.ts create mode 100644 jslib/common/src/models/api/secureNoteApi.ts create mode 100644 jslib/common/src/models/api/sendFileApi.ts create mode 100644 jslib/common/src/models/api/sendTextApi.ts create mode 100644 jslib/common/src/models/api/ssoConfigApi.ts create mode 100644 jslib/common/src/models/data/attachmentData.ts create mode 100644 jslib/common/src/models/data/cardData.ts create mode 100644 jslib/common/src/models/data/cipherData.ts create mode 100644 jslib/common/src/models/data/collectionData.ts create mode 100644 jslib/common/src/models/data/eventData.ts create mode 100644 jslib/common/src/models/data/fieldData.ts create mode 100644 jslib/common/src/models/data/folderData.ts create mode 100644 jslib/common/src/models/data/identityData.ts create mode 100644 jslib/common/src/models/data/loginData.ts create mode 100644 jslib/common/src/models/data/loginUriData.ts create mode 100644 jslib/common/src/models/data/organizationData.ts create mode 100644 jslib/common/src/models/data/passwordHistoryData.ts create mode 100644 jslib/common/src/models/data/policyData.ts create mode 100644 jslib/common/src/models/data/providerData.ts create mode 100644 jslib/common/src/models/data/secureNoteData.ts create mode 100644 jslib/common/src/models/data/sendData.ts create mode 100644 jslib/common/src/models/data/sendFileData.ts create mode 100644 jslib/common/src/models/data/sendTextData.ts create mode 100644 jslib/common/src/models/domain/account.ts create mode 100644 jslib/common/src/models/domain/attachment.ts create mode 100644 jslib/common/src/models/domain/authResult.ts create mode 100644 jslib/common/src/models/domain/card.ts create mode 100644 jslib/common/src/models/domain/cipher.ts create mode 100644 jslib/common/src/models/domain/collection.ts create mode 100644 jslib/common/src/models/domain/decryptParameters.ts create mode 100644 jslib/common/src/models/domain/domainBase.ts create mode 100644 jslib/common/src/models/domain/encArrayBuffer.ts create mode 100644 jslib/common/src/models/domain/encString.ts create mode 100644 jslib/common/src/models/domain/encryptedObject.ts create mode 100644 jslib/common/src/models/domain/environmentUrls.ts create mode 100644 jslib/common/src/models/domain/field.ts create mode 100644 jslib/common/src/models/domain/folder.ts create mode 100644 jslib/common/src/models/domain/generatedPasswordHistory.ts create mode 100644 jslib/common/src/models/domain/globalState.ts create mode 100644 jslib/common/src/models/domain/identity.ts create mode 100644 jslib/common/src/models/domain/importResult.ts create mode 100644 jslib/common/src/models/domain/logInCredentials.ts create mode 100644 jslib/common/src/models/domain/login.ts create mode 100644 jslib/common/src/models/domain/loginUri.ts create mode 100644 jslib/common/src/models/domain/masterPasswordPolicyOptions.ts create mode 100644 jslib/common/src/models/domain/organization.ts create mode 100644 jslib/common/src/models/domain/password.ts create mode 100644 jslib/common/src/models/domain/passwordGeneratorPolicyOptions.ts create mode 100644 jslib/common/src/models/domain/policy.ts create mode 100644 jslib/common/src/models/domain/provider.ts create mode 100644 jslib/common/src/models/domain/resetPasswordPolicyOptions.ts create mode 100644 jslib/common/src/models/domain/secureNote.ts create mode 100644 jslib/common/src/models/domain/send.ts create mode 100644 jslib/common/src/models/domain/sendAccess.ts create mode 100644 jslib/common/src/models/domain/sendFile.ts create mode 100644 jslib/common/src/models/domain/sendText.ts create mode 100644 jslib/common/src/models/domain/sortedCiphersCache.ts create mode 100644 jslib/common/src/models/domain/state.ts create mode 100644 jslib/common/src/models/domain/storageOptions.ts create mode 100644 jslib/common/src/models/domain/symmetricCryptoKey.ts create mode 100644 jslib/common/src/models/domain/treeNode.ts create mode 100644 jslib/common/src/models/domain/windowState.ts create mode 100644 jslib/common/src/models/export/card.ts create mode 100644 jslib/common/src/models/export/cipher.ts create mode 100644 jslib/common/src/models/export/cipherWithIds.ts create mode 100644 jslib/common/src/models/export/collection.ts create mode 100644 jslib/common/src/models/export/collectionWithId.ts create mode 100644 jslib/common/src/models/export/event.ts create mode 100644 jslib/common/src/models/export/field.ts create mode 100644 jslib/common/src/models/export/folder.ts create mode 100644 jslib/common/src/models/export/folderWithId.ts create mode 100644 jslib/common/src/models/export/identity.ts create mode 100644 jslib/common/src/models/export/login.ts create mode 100644 jslib/common/src/models/export/loginUri.ts create mode 100644 jslib/common/src/models/export/secureNote.ts create mode 100644 jslib/common/src/models/request/account/setKeyConnectorKeyRequest.ts create mode 100644 jslib/common/src/models/request/account/verifyOTPRequest.ts create mode 100644 jslib/common/src/models/request/attachmentRequest.ts create mode 100644 jslib/common/src/models/request/bitPayInvoiceRequest.ts create mode 100644 jslib/common/src/models/request/captchaProtectedRequest.ts create mode 100644 jslib/common/src/models/request/cipherBulkDeleteRequest.ts create mode 100644 jslib/common/src/models/request/cipherBulkMoveRequest.ts create mode 100644 jslib/common/src/models/request/cipherBulkRestoreRequest.ts create mode 100644 jslib/common/src/models/request/cipherBulkShareRequest.ts create mode 100644 jslib/common/src/models/request/cipherCollectionsRequest.ts create mode 100644 jslib/common/src/models/request/cipherCreateRequest.ts create mode 100644 jslib/common/src/models/request/cipherRequest.ts create mode 100644 jslib/common/src/models/request/cipherShareRequest.ts create mode 100644 jslib/common/src/models/request/cipherWithIdRequest.ts create mode 100644 jslib/common/src/models/request/collectionRequest.ts create mode 100644 jslib/common/src/models/request/deleteRecoverRequest.ts create mode 100644 jslib/common/src/models/request/deviceRequest.ts create mode 100644 jslib/common/src/models/request/deviceTokenRequest.ts create mode 100644 jslib/common/src/models/request/emailRequest.ts create mode 100644 jslib/common/src/models/request/emailTokenRequest.ts create mode 100644 jslib/common/src/models/request/emergencyAccessAcceptRequest.ts create mode 100644 jslib/common/src/models/request/emergencyAccessConfirmRequest.ts create mode 100644 jslib/common/src/models/request/emergencyAccessInviteRequest.ts create mode 100644 jslib/common/src/models/request/emergencyAccessPasswordRequest.ts create mode 100644 jslib/common/src/models/request/emergencyAccessUpdateRequest.ts create mode 100644 jslib/common/src/models/request/eventRequest.ts create mode 100644 jslib/common/src/models/request/folderRequest.ts create mode 100644 jslib/common/src/models/request/folderWithIdRequest.ts create mode 100644 jslib/common/src/models/request/groupRequest.ts create mode 100644 jslib/common/src/models/request/iapCheckRequest.ts create mode 100644 jslib/common/src/models/request/identityToken/apiTokenRequest.ts create mode 100644 jslib/common/src/models/request/identityToken/passwordTokenRequest.ts create mode 100644 jslib/common/src/models/request/identityToken/ssoTokenRequest.ts create mode 100644 jslib/common/src/models/request/identityToken/tokenRequest.ts create mode 100644 jslib/common/src/models/request/identityToken/tokenRequestTwoFactor.ts create mode 100644 jslib/common/src/models/request/importCiphersRequest.ts create mode 100644 jslib/common/src/models/request/importDirectoryRequest.ts create mode 100644 jslib/common/src/models/request/importDirectoryRequestGroup.ts create mode 100644 jslib/common/src/models/request/importDirectoryRequestUser.ts create mode 100644 jslib/common/src/models/request/importOrganizationCiphersRequest.ts create mode 100644 jslib/common/src/models/request/kdfRequest.ts create mode 100644 jslib/common/src/models/request/keyConnectorUserKeyRequest.ts create mode 100644 jslib/common/src/models/request/keysRequest.ts create mode 100644 jslib/common/src/models/request/kvpRequest.ts create mode 100644 jslib/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts create mode 100644 jslib/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts create mode 100644 jslib/common/src/models/request/organization/organizationSsoRequest.ts create mode 100644 jslib/common/src/models/request/organizationCreateRequest.ts create mode 100644 jslib/common/src/models/request/organizationImportGroupRequest.ts create mode 100644 jslib/common/src/models/request/organizationImportMemberRequest.ts create mode 100644 jslib/common/src/models/request/organizationImportRequest.ts create mode 100644 jslib/common/src/models/request/organizationKeysRequest.ts create mode 100644 jslib/common/src/models/request/organizationSubscriptionUpdateRequest.ts create mode 100644 jslib/common/src/models/request/organizationTaxInfoUpdateRequest.ts create mode 100644 jslib/common/src/models/request/organizationUpdateRequest.ts create mode 100644 jslib/common/src/models/request/organizationUpgradeRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserAcceptRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserBulkConfirmRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserBulkRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserConfirmRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserInviteRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserResetPasswordEnrollmentRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserResetPasswordRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserUpdateGroupsRequest.ts create mode 100644 jslib/common/src/models/request/organizationUserUpdateRequest.ts create mode 100644 jslib/common/src/models/request/passwordHintRequest.ts create mode 100644 jslib/common/src/models/request/passwordHistoryRequest.ts create mode 100644 jslib/common/src/models/request/passwordRequest.ts create mode 100644 jslib/common/src/models/request/paymentRequest.ts create mode 100644 jslib/common/src/models/request/policyRequest.ts create mode 100644 jslib/common/src/models/request/preloginRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerAddOrganizationRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerOrganizationCreateRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerSetupRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerUpdateRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerUserAcceptRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerUserBulkConfirmRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerUserBulkRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerUserConfirmRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerUserInviteRequest.ts create mode 100644 jslib/common/src/models/request/provider/providerUserUpdateRequest.ts create mode 100644 jslib/common/src/models/request/referenceEventRequest.ts create mode 100644 jslib/common/src/models/request/registerRequest.ts create mode 100644 jslib/common/src/models/request/seatRequest.ts create mode 100644 jslib/common/src/models/request/secretVerificationRequest.ts create mode 100644 jslib/common/src/models/request/selectionReadOnlyRequest.ts create mode 100644 jslib/common/src/models/request/sendAccessRequest.ts create mode 100644 jslib/common/src/models/request/sendRequest.ts create mode 100644 jslib/common/src/models/request/sendWithIdRequest.ts create mode 100644 jslib/common/src/models/request/setPasswordRequest.ts create mode 100644 jslib/common/src/models/request/storageRequest.ts create mode 100644 jslib/common/src/models/request/taxInfoUpdateRequest.ts create mode 100644 jslib/common/src/models/request/twoFactorEmailRequest.ts create mode 100644 jslib/common/src/models/request/twoFactorProviderRequest.ts create mode 100644 jslib/common/src/models/request/twoFactorRecoveryRequest.ts create mode 100644 jslib/common/src/models/request/updateDomainsRequest.ts create mode 100644 jslib/common/src/models/request/updateKeyRequest.ts create mode 100644 jslib/common/src/models/request/updateProfileRequest.ts create mode 100644 jslib/common/src/models/request/updateTempPasswordRequest.ts create mode 100644 jslib/common/src/models/request/updateTwoFactorAuthenticatorRequest.ts create mode 100644 jslib/common/src/models/request/updateTwoFactorDuoRequest.ts create mode 100644 jslib/common/src/models/request/updateTwoFactorEmailRequest.ts create mode 100644 jslib/common/src/models/request/updateTwoFactorWebAuthnDeleteRequest.ts create mode 100644 jslib/common/src/models/request/updateTwoFactorWebAuthnRequest.ts create mode 100644 jslib/common/src/models/request/updateTwoFactorYubioOtpRequest.ts create mode 100644 jslib/common/src/models/request/verifyBankRequest.ts create mode 100644 jslib/common/src/models/request/verifyDeleteRecoverRequest.ts create mode 100644 jslib/common/src/models/request/verifyEmailRequest.ts create mode 100644 jslib/common/src/models/response/apiKeyResponse.ts create mode 100644 jslib/common/src/models/response/attachmentResponse.ts create mode 100644 jslib/common/src/models/response/attachmentUploadDataResponse.ts create mode 100644 jslib/common/src/models/response/baseResponse.ts create mode 100644 jslib/common/src/models/response/billingResponse.ts create mode 100644 jslib/common/src/models/response/breachAccountResponse.ts create mode 100644 jslib/common/src/models/response/cipherResponse.ts create mode 100644 jslib/common/src/models/response/collectionResponse.ts create mode 100644 jslib/common/src/models/response/deviceResponse.ts create mode 100644 jslib/common/src/models/response/domainsResponse.ts create mode 100644 jslib/common/src/models/response/emergencyAccessResponse.ts create mode 100644 jslib/common/src/models/response/errorResponse.ts create mode 100644 jslib/common/src/models/response/eventResponse.ts create mode 100644 jslib/common/src/models/response/folderResponse.ts create mode 100644 jslib/common/src/models/response/globalDomainResponse.ts create mode 100644 jslib/common/src/models/response/groupResponse.ts create mode 100644 jslib/common/src/models/response/identityCaptchaResponse.ts create mode 100644 jslib/common/src/models/response/identityTokenResponse.ts create mode 100644 jslib/common/src/models/response/identityTwoFactorResponse.ts create mode 100644 jslib/common/src/models/response/keyConnectorUserKeyResponse.ts create mode 100644 jslib/common/src/models/response/keysResponse.ts create mode 100644 jslib/common/src/models/response/listResponse.ts create mode 100644 jslib/common/src/models/response/notificationResponse.ts create mode 100644 jslib/common/src/models/response/organization/organizationSsoResponse.ts create mode 100644 jslib/common/src/models/response/organizationAutoEnrollStatusResponse.ts create mode 100644 jslib/common/src/models/response/organizationKeysResponse.ts create mode 100644 jslib/common/src/models/response/organizationResponse.ts create mode 100644 jslib/common/src/models/response/organizationSubscriptionResponse.ts create mode 100644 jslib/common/src/models/response/organizationUserBulkPublicKeyResponse.ts create mode 100644 jslib/common/src/models/response/organizationUserBulkResponse.ts create mode 100644 jslib/common/src/models/response/organizationUserResponse.ts create mode 100644 jslib/common/src/models/response/passwordHistoryResponse.ts create mode 100644 jslib/common/src/models/response/paymentResponse.ts create mode 100644 jslib/common/src/models/response/planResponse.ts create mode 100644 jslib/common/src/models/response/policyResponse.ts create mode 100644 jslib/common/src/models/response/preloginResponse.ts create mode 100644 jslib/common/src/models/response/profileOrganizationResponse.ts create mode 100644 jslib/common/src/models/response/profileProviderOrganizationResponse.ts create mode 100644 jslib/common/src/models/response/profileProviderResponse.ts create mode 100644 jslib/common/src/models/response/profileResponse.ts create mode 100644 jslib/common/src/models/response/provider/providerOrganizationResponse.ts create mode 100644 jslib/common/src/models/response/provider/providerResponse.ts create mode 100644 jslib/common/src/models/response/provider/providerUserBulkPublicKeyResponse.ts create mode 100644 jslib/common/src/models/response/provider/providerUserBulkResponse.ts create mode 100644 jslib/common/src/models/response/provider/providerUserResponse.ts create mode 100644 jslib/common/src/models/response/selectionReadOnlyResponse.ts create mode 100644 jslib/common/src/models/response/sendAccessResponse.ts create mode 100644 jslib/common/src/models/response/sendFileDownloadDataResponse.ts create mode 100644 jslib/common/src/models/response/sendFileUploadDataResponse.ts create mode 100644 jslib/common/src/models/response/sendResponse.ts create mode 100644 jslib/common/src/models/response/subscriptionResponse.ts create mode 100644 jslib/common/src/models/response/syncResponse.ts create mode 100644 jslib/common/src/models/response/taxInfoResponse.ts create mode 100644 jslib/common/src/models/response/taxRateResponse.ts create mode 100644 jslib/common/src/models/response/twoFactorAuthenticatorResponse.ts create mode 100644 jslib/common/src/models/response/twoFactorDuoResponse.ts create mode 100644 jslib/common/src/models/response/twoFactorEmailResponse.ts create mode 100644 jslib/common/src/models/response/twoFactorProviderResponse.ts create mode 100644 jslib/common/src/models/response/twoFactorRescoverResponse.ts create mode 100644 jslib/common/src/models/response/twoFactorWebAuthnResponse.ts create mode 100644 jslib/common/src/models/response/twoFactorYubiKeyResponse.ts create mode 100644 jslib/common/src/models/response/userKeyResponse.ts create mode 100644 jslib/common/src/models/view/attachmentView.ts create mode 100644 jslib/common/src/models/view/cardView.ts create mode 100644 jslib/common/src/models/view/cipherView.ts create mode 100644 jslib/common/src/models/view/collectionView.ts create mode 100644 jslib/common/src/models/view/eventView.ts create mode 100644 jslib/common/src/models/view/fieldView.ts create mode 100644 jslib/common/src/models/view/folderView.ts create mode 100644 jslib/common/src/models/view/identityView.ts create mode 100644 jslib/common/src/models/view/itemView.ts create mode 100644 jslib/common/src/models/view/loginUriView.ts create mode 100644 jslib/common/src/models/view/loginView.ts create mode 100644 jslib/common/src/models/view/passwordHistoryView.ts create mode 100644 jslib/common/src/models/view/secureNoteView.ts create mode 100644 jslib/common/src/models/view/sendAccessView.ts create mode 100644 jslib/common/src/models/view/sendFileView.ts create mode 100644 jslib/common/src/models/view/sendTextView.ts create mode 100644 jslib/common/src/models/view/sendView.ts create mode 100644 jslib/common/src/models/view/ssoConfigView.ts create mode 100644 jslib/common/src/models/view/view.ts create mode 100644 jslib/common/src/services/api.service.ts create mode 100644 jslib/common/src/services/appId.service.ts create mode 100644 jslib/common/src/services/audit.service.ts create mode 100644 jslib/common/src/services/auth.service.ts create mode 100644 jslib/common/src/services/azureFileUpload.service.ts create mode 100644 jslib/common/src/services/bitwardenFileUpload.service.ts create mode 100644 jslib/common/src/services/broadcaster.service.ts create mode 100644 jslib/common/src/services/cipher.service.ts create mode 100644 jslib/common/src/services/collection.service.ts create mode 100644 jslib/common/src/services/consoleLog.service.ts create mode 100644 jslib/common/src/services/container.service.ts create mode 100644 jslib/common/src/services/crypto.service.ts create mode 100644 jslib/common/src/services/environment.service.ts create mode 100644 jslib/common/src/services/event.service.ts create mode 100644 jslib/common/src/services/export.service.ts create mode 100644 jslib/common/src/services/fileUpload.service.ts create mode 100644 jslib/common/src/services/folder.service.ts create mode 100644 jslib/common/src/services/i18n.service.ts create mode 100644 jslib/common/src/services/import.service.ts create mode 100644 jslib/common/src/services/keyConnector.service.ts create mode 100644 jslib/common/src/services/noopMessaging.service.ts create mode 100644 jslib/common/src/services/notifications.service.ts create mode 100644 jslib/common/src/services/organization.service.ts create mode 100644 jslib/common/src/services/passwordGeneration.service.ts create mode 100644 jslib/common/src/services/policy.service.ts create mode 100644 jslib/common/src/services/provider.service.ts create mode 100644 jslib/common/src/services/search.service.ts create mode 100644 jslib/common/src/services/send.service.ts create mode 100644 jslib/common/src/services/settings.service.ts create mode 100644 jslib/common/src/services/state.service.ts create mode 100644 jslib/common/src/services/stateMigration.service.ts create mode 100644 jslib/common/src/services/sync.service.ts create mode 100644 jslib/common/src/services/system.service.ts create mode 100644 jslib/common/src/services/token.service.ts create mode 100644 jslib/common/src/services/totp.service.ts create mode 100644 jslib/common/src/services/twoFactor.service.ts create mode 100644 jslib/common/src/services/userVerification.service.ts create mode 100644 jslib/common/src/services/usernameGeneration.service.ts create mode 100644 jslib/common/src/services/vaultTimeout.service.ts create mode 100644 jslib/common/src/services/webCryptoFunction.service.ts create mode 100644 jslib/common/src/types/verification.ts create mode 100644 jslib/common/tsconfig.json create mode 100644 jslib/common/tsconfig.spec.json create mode 100644 jslib/electron/jest.config.js create mode 100644 jslib/electron/package-lock.json create mode 100644 jslib/electron/package.json create mode 100644 jslib/electron/spec/services/electronLog.service.spec.ts create mode 100644 jslib/electron/spec/test.ts create mode 100644 jslib/electron/spec/utils.spec.ts create mode 100644 jslib/electron/src/baseMenu.ts create mode 100644 jslib/electron/src/globals.d.ts create mode 100644 jslib/electron/src/services/electronCrypto.service.ts create mode 100644 jslib/electron/src/services/electronLog.service.ts create mode 100644 jslib/electron/src/services/electronMainMessaging.service.ts create mode 100644 jslib/electron/src/services/electronPlatformUtils.service.ts create mode 100644 jslib/electron/src/services/electronRendererMessaging.service.ts create mode 100644 jslib/electron/src/services/electronRendererSecureStorage.service.ts create mode 100644 jslib/electron/src/services/electronRendererStorage.service.ts create mode 100644 jslib/electron/src/services/electronStorage.service.ts create mode 100644 jslib/electron/src/tray.main.ts create mode 100644 jslib/electron/src/updater.main.ts create mode 100644 jslib/electron/src/utils.ts create mode 100644 jslib/electron/src/window.main.ts create mode 100644 jslib/electron/tsconfig.json create mode 100644 jslib/electron/tsconfig.spec.json create mode 100644 jslib/jest.config.js create mode 100644 jslib/node/jest.config.js create mode 100644 jslib/node/package-lock.json create mode 100644 jslib/node/package.json create mode 100644 jslib/node/spec/cli/consoleLog.service.spec.ts create mode 100644 jslib/node/spec/services/nodeCryptoFunction.service.spec.ts create mode 100644 jslib/node/spec/test.ts create mode 100644 jslib/node/src/cli/baseProgram.ts create mode 100644 jslib/node/src/cli/commands/login.command.ts create mode 100644 jslib/node/src/cli/commands/logout.command.ts create mode 100644 jslib/node/src/cli/commands/update.command.ts create mode 100644 jslib/node/src/cli/models/response.ts create mode 100644 jslib/node/src/cli/models/response/baseResponse.ts create mode 100644 jslib/node/src/cli/models/response/fileResponse.ts create mode 100644 jslib/node/src/cli/models/response/listResponse.ts create mode 100644 jslib/node/src/cli/models/response/messageResponse.ts create mode 100644 jslib/node/src/cli/models/response/stringResponse.ts create mode 100644 jslib/node/src/cli/services/cliPlatformUtils.service.ts create mode 100644 jslib/node/src/cli/services/consoleLog.service.ts create mode 100644 jslib/node/src/globals.d.ts create mode 100644 jslib/node/src/services/lowdbStorage.service.ts create mode 100644 jslib/node/src/services/nodeApi.service.ts create mode 100644 jslib/node/src/services/nodeCryptoFunction.service.ts create mode 100644 jslib/node/tsconfig.json create mode 100644 jslib/node/tsconfig.spec.json create mode 100644 jslib/package-lock.json create mode 100644 jslib/package.json create mode 100644 jslib/shared/eslintrc.json create mode 100644 jslib/shared/tsconfig.json create mode 100644 jslib/tsconfig.json create mode 100644 jslib/tsconfig.spec.json diff --git a/.eslintignore b/.eslintignore index 8ca8ab63..cbcaffc1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,9 +1,10 @@ dist build build-cli -jslib webpack.cli.js webpack.main.js webpack.renderer.js **/node_modules + +**/jest.config.js diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 31ee66bd..00000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "jslib"] - path = jslib - url = https://github.com/bitwarden/jslib.git - branch = master diff --git a/.prettierignore b/.prettierignore index 4842e48e..b19eb366 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,8 +3,6 @@ build build-cli dist -jslib - # External libraries / auto synced locales src/locales diff --git a/jslib b/jslib deleted file mode 160000 index 6bcadc4f..00000000 --- a/jslib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6bcadc4f408db2c150753f53a07d6f8888b6e9ff diff --git a/jslib/.gitignore b/jslib/.gitignore new file mode 100644 index 00000000..20b78636 --- /dev/null +++ b/jslib/.gitignore @@ -0,0 +1,9 @@ +.vs +.idea +node_modules +npm-debug.log +vwd.webinfo +*.crx +*.pem +dist +coverage diff --git a/jslib/.prettierrc.json b/jslib/.prettierrc.json new file mode 100644 index 00000000..de753c53 --- /dev/null +++ b/jslib/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "printWidth": 100 +} diff --git a/jslib/angular/jest.config.js b/jslib/angular/jest.config.js new file mode 100644 index 00000000..19d250fc --- /dev/null +++ b/jslib/angular/jest.config.js @@ -0,0 +1,17 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + name: "angular", + displayName: "angular tests", + preset: "jest-preset-angular", + testMatch: ["**/+(*.)+(spec).+(ts)"], + setupFilesAfterEnv: ["/spec/test.ts"], + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/jslib/angular/package-lock.json b/jslib/angular/package-lock.json new file mode 100644 index 00000000..7b2465bf --- /dev/null +++ b/jslib/angular/package-lock.json @@ -0,0 +1,654 @@ +{ + "name": "@bitwarden/jslib-angular", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib-angular", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@angular/animations": "^12.2.13", + "@angular/cdk": "^12.2.13", + "@angular/common": "^12.2.13", + "@angular/compiler": "^12.2.13", + "@angular/core": "^12.2.13", + "@angular/forms": "^12.2.13", + "@angular/platform-browser": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@angular/router": "^12.2.13", + "@bitwarden/jslib-common": "file:../common", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zone.js": "0.11.4" + }, + "devDependencies": { + "@types/duo_web_sdk": "^2.7.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "../common": { + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@angular/animations": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.14.tgz", + "integrity": "sha512-1BR5u32auVePvXNNP96DB2008V+Ku0OGqeZQl2h4XA9xzES/Zk5WllIJZXqRmWMRBVARfXsfb0RdMty9gcaVjA==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/core": "12.2.14" + } + }, + "node_modules/@angular/cdk": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz", + "integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^12.0.0 || ^13.0.0-0", + "@angular/core": "^12.0.0 || ^13.0.0-0", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/common": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.14.tgz", + "integrity": "sha512-ffYUYdwZETmFJw0AcWY30WsaWBhJxj/zSmFXWjgEGEGZH56zwbbNwfMZOYZ1jz4haAVxGu+TdXsOl2yMGzN7jQ==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/core": "12.2.14", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/compiler": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.14.tgz", + "integrity": "sha512-dwmZi+n66IUzRFlGWu9mjXq170ZEsaDvlNLZzaPgs6vZTa4Kt7PWvIF/Y7TMvnVv/uqNG6kOhfmOkf6rfz1Gjg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + } + }, + "node_modules/@angular/core": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.14.tgz", + "integrity": "sha512-dlVk7yqUHL2R/eCmM8LsWuxhEBfzg0y1zHt0UqCuFwlCoiw+IG4HFy4OlZEUw9NUEZJSv0aDv3sWqxLkvK5vvg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.0.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/forms": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.14.tgz", + "integrity": "sha512-/9/gSJUBXVRVdRnzgJnALAQZYJATuGDMkFC9ms9DEMG4PMAhe9x4if1lJjN6noz5RAom3qNuVBNWaYAPUxlcBQ==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.14", + "@angular/core": "12.2.14", + "@angular/platform-browser": "12.2.14", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.14.tgz", + "integrity": "sha512-fWcE2rnJ3ZCISa1oPfsIDV7FBZBoLFEdDuMXAiDYqDPKvF/E5U5nHrS+K4SlLAi094bMobtTOReNWl/Ienniyw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/animations": "12.2.14", + "@angular/common": "12.2.14", + "@angular/core": "12.2.14" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.14.tgz", + "integrity": "sha512-0NPF7mS91Tct8rBmOLZPmnLSuS4kbLHXo6eTgrg80OC0vlzBiQwGDVW4X3KncCoX9CpevaGJCdSMc+uPNsFOUQ==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.14", + "@angular/compiler": "12.2.14", + "@angular/core": "12.2.14", + "@angular/platform-browser": "12.2.14" + } + }, + "node_modules/@angular/router": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.14.tgz", + "integrity": "sha512-yP5grSnqBvc4vNhtYdcxDgDYIebUKs5f0xyFkUJM5030UnQ0CV45tBsSxHMkQbPZucIfOuxpRy8xy5+4GizuwQ==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.14", + "@angular/core": "12.2.14", + "@angular/platform-browser": "12.2.14", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@bitwarden/jslib-common": { + "resolved": "../common", + "link": true + }, + "node_modules/@types/duo_web_sdk": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz", + "integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/duo_web_sdk": { + "version": "2.7.0", + "resolved": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rxjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "dependencies": { + "tslib": "~2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + }, + "node_modules/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "hasInstallScript": true, + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "dependencies": { + "tslib": "^2.0.0" + } + } + }, + "dependencies": { + "@angular/animations": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.14.tgz", + "integrity": "sha512-1BR5u32auVePvXNNP96DB2008V+Ku0OGqeZQl2h4XA9xzES/Zk5WllIJZXqRmWMRBVARfXsfb0RdMty9gcaVjA==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/cdk": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz", + "integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.2.0" + } + }, + "@angular/common": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.14.tgz", + "integrity": "sha512-ffYUYdwZETmFJw0AcWY30WsaWBhJxj/zSmFXWjgEGEGZH56zwbbNwfMZOYZ1jz4haAVxGu+TdXsOl2yMGzN7jQ==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/compiler": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.14.tgz", + "integrity": "sha512-dwmZi+n66IUzRFlGWu9mjXq170ZEsaDvlNLZzaPgs6vZTa4Kt7PWvIF/Y7TMvnVv/uqNG6kOhfmOkf6rfz1Gjg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/core": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.14.tgz", + "integrity": "sha512-dlVk7yqUHL2R/eCmM8LsWuxhEBfzg0y1zHt0UqCuFwlCoiw+IG4HFy4OlZEUw9NUEZJSv0aDv3sWqxLkvK5vvg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/forms": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.14.tgz", + "integrity": "sha512-/9/gSJUBXVRVdRnzgJnALAQZYJATuGDMkFC9ms9DEMG4PMAhe9x4if1lJjN6noz5RAom3qNuVBNWaYAPUxlcBQ==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/platform-browser": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.14.tgz", + "integrity": "sha512-fWcE2rnJ3ZCISa1oPfsIDV7FBZBoLFEdDuMXAiDYqDPKvF/E5U5nHrS+K4SlLAi094bMobtTOReNWl/Ienniyw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.14.tgz", + "integrity": "sha512-0NPF7mS91Tct8rBmOLZPmnLSuS4kbLHXo6eTgrg80OC0vlzBiQwGDVW4X3KncCoX9CpevaGJCdSMc+uPNsFOUQ==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/router": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.14.tgz", + "integrity": "sha512-yP5grSnqBvc4vNhtYdcxDgDYIebUKs5f0xyFkUJM5030UnQ0CV45tBsSxHMkQbPZucIfOuxpRy8xy5+4GizuwQ==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@bitwarden/jslib-common": { + "version": "file:../common", + "requires": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zxcvbn": "^4.4.2" + } + }, + "@types/duo_web_sdk": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz", + "integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "duo_web_sdk": { + "version": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda", + "from": "duo_web_sdk@git+https://github.com/duosecurity/duo_web_sdk.git" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rxjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "requires": { + "tslib": "~2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + }, + "tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "requires": { + "punycode": "^1.4.1" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "requires": { + "tslib": "^2.0.0" + } + } + } +} diff --git a/jslib/angular/package.json b/jslib/angular/package.json new file mode 100644 index 00000000..47cfa4a4 --- /dev/null +++ b/jslib/angular/package.json @@ -0,0 +1,41 @@ +{ + "name": "@bitwarden/jslib-angular", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + }, + "devDependencies": { + "@types/duo_web_sdk": "^2.7.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "@angular/animations": "^12.2.13", + "@angular/cdk": "^12.2.13", + "@angular/common": "^12.2.13", + "@angular/compiler": "^12.2.13", + "@angular/core": "^12.2.13", + "@angular/forms": "^12.2.13", + "@angular/platform-browser": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@angular/router": "^12.2.13", + "@bitwarden/jslib-common": "file:../common", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zone.js": "0.11.4" + } +} diff --git a/jslib/angular/spec/test.ts b/jslib/angular/spec/test.ts new file mode 100644 index 00000000..6be6e7b8 --- /dev/null +++ b/jslib/angular/spec/test.ts @@ -0,0 +1,28 @@ +import { webcrypto } from "crypto"; +import "jest-preset-angular/setup-jest"; + +Object.defineProperty(window, "CSS", { value: null }); +Object.defineProperty(window, "getComputedStyle", { + value: () => { + return { + display: "none", + appearance: ["-webkit-appearance"], + }; + }, +}); + +Object.defineProperty(document, "doctype", { + value: "", +}); +Object.defineProperty(document.body.style, "transform", { + value: () => { + return { + enumerable: true, + configurable: true, + }; + }, +}); + +Object.defineProperty(window, "crypto", { + value: webcrypto, +}); diff --git a/jslib/angular/src/components/callout.component.html b/jslib/angular/src/components/callout.component.html new file mode 100644 index 00000000..a049d5cb --- /dev/null +++ b/jslib/angular/src/components/callout.component.html @@ -0,0 +1,35 @@ +
+

+ + {{ title }} +

+
+ {{ enforcedPolicyMessage }} +
    +
  • + {{ "policyInEffectMinComplexity" | i18n: getPasswordScoreAlertDisplay() }} +
  • +
  • + {{ "policyInEffectMinLength" | i18n: enforcedPolicyOptions?.minLength.toString() }} +
  • +
  • + {{ "policyInEffectUppercase" | i18n }} +
  • +
  • + {{ "policyInEffectLowercase" | i18n }} +
  • +
  • + {{ "policyInEffectNumbers" | i18n }} +
  • +
  • + {{ "policyInEffectSpecial" | i18n: "!@#$%^&*" }} +
  • +
+
+ +
diff --git a/jslib/angular/src/components/callout.component.ts b/jslib/angular/src/components/callout.component.ts new file mode 100644 index 00000000..a1785fe5 --- /dev/null +++ b/jslib/angular/src/components/callout.component.ts @@ -0,0 +1,78 @@ +import { Component, Input, OnInit } from "@angular/core"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions"; + +@Component({ + selector: "app-callout", + templateUrl: "callout.component.html", +}) +export class CalloutComponent implements OnInit { + @Input() type = "info"; + @Input() icon: string; + @Input() title: string; + @Input() clickable: boolean; + @Input() enforcedPolicyOptions: MasterPasswordPolicyOptions; + @Input() enforcedPolicyMessage: string; + @Input() useAlertRole = false; + + calloutStyle: string; + + constructor(private i18nService: I18nService) {} + + ngOnInit() { + this.calloutStyle = this.type; + + if (this.enforcedPolicyMessage === undefined) { + this.enforcedPolicyMessage = this.i18nService.t("masterPasswordPolicyInEffect"); + } + + if (this.type === "warning" || this.type === "danger") { + if (this.type === "danger") { + this.calloutStyle = "danger"; + } + if (this.title === undefined) { + this.title = this.i18nService.t("warning"); + } + if (this.icon === undefined) { + this.icon = "bwi-exclamation-triangle"; + } + } else if (this.type === "error") { + this.calloutStyle = "danger"; + if (this.title === undefined) { + this.title = this.i18nService.t("error"); + } + if (this.icon === undefined) { + this.icon = "bwi-error"; + } + } else if (this.type === "tip") { + this.calloutStyle = "success"; + if (this.title === undefined) { + this.title = this.i18nService.t("tip"); + } + if (this.icon === undefined) { + this.icon = "bwi-lightbulb"; + } + } + } + + getPasswordScoreAlertDisplay() { + if (this.enforcedPolicyOptions == null) { + return ""; + } + + let str: string; + switch (this.enforcedPolicyOptions.minComplexity) { + case 4: + str = this.i18nService.t("strong"); + break; + case 3: + str = this.i18nService.t("good"); + break; + default: + str = this.i18nService.t("weak"); + break; + } + return str + " (" + this.enforcedPolicyOptions.minComplexity + ")"; + } +} diff --git a/jslib/angular/src/components/environment.component.ts b/jslib/angular/src/components/environment.component.ts new file mode 100644 index 00000000..8b7689de --- /dev/null +++ b/jslib/angular/src/components/environment.component.ts @@ -0,0 +1,63 @@ +import { Directive, EventEmitter, Output } from "@angular/core"; + +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; + +@Directive() +export class EnvironmentComponent { + @Output() onSaved = new EventEmitter(); + + iconsUrl: string; + identityUrl: string; + apiUrl: string; + webVaultUrl: string; + notificationsUrl: string; + baseUrl: string; + showCustom = false; + + constructor( + protected platformUtilsService: PlatformUtilsService, + protected environmentService: EnvironmentService, + protected i18nService: I18nService + ) { + const urls = this.environmentService.getUrls(); + + this.baseUrl = urls.base || ""; + this.webVaultUrl = urls.webVault || ""; + this.apiUrl = urls.api || ""; + this.identityUrl = urls.identity || ""; + this.iconsUrl = urls.icons || ""; + this.notificationsUrl = urls.notifications || ""; + } + + async submit() { + const resUrls = await this.environmentService.setUrls({ + base: this.baseUrl, + api: this.apiUrl, + identity: this.identityUrl, + webVault: this.webVaultUrl, + icons: this.iconsUrl, + notifications: this.notificationsUrl, + }); + + // re-set urls since service can change them, ex: prefixing https:// + this.baseUrl = resUrls.base; + this.apiUrl = resUrls.api; + this.identityUrl = resUrls.identity; + this.webVaultUrl = resUrls.webVault; + this.iconsUrl = resUrls.icons; + this.notificationsUrl = resUrls.notifications; + + this.platformUtilsService.showToast("success", null, this.i18nService.t("environmentSaved")); + this.saved(); + } + + toggleCustom() { + this.showCustom = !this.showCustom; + } + + protected saved() { + this.onSaved.emit(); + } +} diff --git a/jslib/angular/src/components/icon.component.html b/jslib/angular/src/components/icon.component.html new file mode 100644 index 00000000..27182fbb --- /dev/null +++ b/jslib/angular/src/components/icon.component.html @@ -0,0 +1,11 @@ + diff --git a/jslib/angular/src/components/icon.component.ts b/jslib/angular/src/components/icon.component.ts new file mode 100644 index 00000000..9cb8e907 --- /dev/null +++ b/jslib/angular/src/components/icon.component.ts @@ -0,0 +1,112 @@ +import { Component, Input, OnChanges } from "@angular/core"; + +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { CipherType } from "jslib-common/enums/cipherType"; +import { Utils } from "jslib-common/misc/utils"; +import { CipherView } from "jslib-common/models/view/cipherView"; + +/** + * Provides a mapping from supported card brands to + * the filenames of icon that should be present in images/cards folder of clients. + */ +const cardIcons: Record = { + Visa: "card-visa", + Mastercard: "card-mastercard", + Amex: "card-amex", + Discover: "card-discover", + "Diners Club": "card-diners-club", + JCB: "card-jcb", + Maestro: "card-maestro", + UnionPay: "card-union-pay", +}; + +@Component({ + selector: "app-vault-icon", + templateUrl: "icon.component.html", +}) +export class IconComponent implements OnChanges { + @Input() cipher: CipherView; + icon: string; + image: string; + fallbackImage: string; + imageEnabled: boolean; + + private iconsUrl: string; + + constructor(environmentService: EnvironmentService, private stateService: StateService) { + this.iconsUrl = environmentService.getIconsUrl(); + } + + async ngOnChanges() { + // Components may be re-used when using cdk-virtual-scroll. Which puts the component in a weird state, + // to avoid this we reset all state variables. + this.image = null; + this.fallbackImage = null; + this.imageEnabled = !(await this.stateService.getDisableFavicon()); + this.load(); + } + + protected load() { + switch (this.cipher.type) { + case CipherType.Login: + this.icon = "bwi-globe"; + this.setLoginIcon(); + break; + case CipherType.SecureNote: + this.icon = "bwi-sticky-note"; + break; + case CipherType.Card: + this.icon = "bwi-credit-card"; + this.setCardIcon(); + break; + case CipherType.Identity: + this.icon = "bwi-id-card"; + break; + default: + break; + } + } + + private setLoginIcon() { + if (this.cipher.login.uri) { + let hostnameUri = this.cipher.login.uri; + let isWebsite = false; + + if (hostnameUri.indexOf("androidapp://") === 0) { + this.icon = "bwi-android"; + this.image = null; + } else if (hostnameUri.indexOf("iosapp://") === 0) { + this.icon = "bwi-apple"; + this.image = null; + } else if ( + this.imageEnabled && + hostnameUri.indexOf("://") === -1 && + hostnameUri.indexOf(".") > -1 + ) { + hostnameUri = "http://" + hostnameUri; + isWebsite = true; + } else if (this.imageEnabled) { + isWebsite = hostnameUri.indexOf("http") === 0 && hostnameUri.indexOf(".") > -1; + } + + if (this.imageEnabled && isWebsite) { + try { + this.image = this.iconsUrl + "/" + Utils.getHostname(hostnameUri) + "/icon.png"; + this.fallbackImage = "images/bwi-globe.png"; + } catch (e) { + // Ignore error since the fallback icon will be shown if image is null. + } + } + } else { + this.image = null; + } + } + + private setCardIcon() { + const brand = this.cipher.card.brand; + if (this.imageEnabled && brand in cardIcons) { + this.icon = "credit-card-icon " + cardIcons[brand]; + } + } +} diff --git a/jslib/angular/src/components/modal/dynamic-modal.component.ts b/jslib/angular/src/components/modal/dynamic-modal.component.ts new file mode 100644 index 00000000..46ad38c0 --- /dev/null +++ b/jslib/angular/src/components/modal/dynamic-modal.component.ts @@ -0,0 +1,79 @@ +import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from "@angular/cdk/a11y"; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ComponentRef, + ElementRef, + OnDestroy, + Type, + ViewChild, + ViewContainerRef, +} from "@angular/core"; + +import { ModalService } from "../../services/modal.service"; + +import { ModalRef } from "./modal.ref"; + +@Component({ + selector: "app-modal", + template: "", +}) +export class DynamicModalComponent implements AfterViewInit, OnDestroy { + componentRef: ComponentRef; + + @ViewChild("modalContent", { read: ViewContainerRef, static: true }) + modalContentRef: ViewContainerRef; + + childComponentType: Type; + setComponentParameters: (component: any) => void; + + private focusTrap: ConfigurableFocusTrap; + + constructor( + private modalService: ModalService, + private cd: ChangeDetectorRef, + private el: ElementRef, + private focusTrapFactory: ConfigurableFocusTrapFactory, + public modalRef: ModalRef + ) {} + + ngAfterViewInit() { + this.loadChildComponent(this.childComponentType); + if (this.setComponentParameters != null) { + this.setComponentParameters(this.componentRef.instance); + } + this.cd.detectChanges(); + + this.modalRef.created(this.el.nativeElement); + this.focusTrap = this.focusTrapFactory.create( + this.el.nativeElement.querySelector(".modal-dialog") + ); + if (this.el.nativeElement.querySelector("[appAutoFocus]") == null) { + this.focusTrap.focusFirstTabbableElementWhenReady(); + } + } + + loadChildComponent(componentType: Type) { + const componentFactory = this.modalService.resolveComponentFactory(componentType); + + this.modalContentRef.clear(); + this.componentRef = this.modalContentRef.createComponent(componentFactory); + } + + ngOnDestroy() { + if (this.componentRef) { + this.componentRef.destroy(); + } + this.focusTrap.destroy(); + } + + close() { + this.modalRef.close(); + } + + getFocus() { + const autoFocusEl = this.el.nativeElement.querySelector("[appAutoFocus]") as HTMLElement; + autoFocusEl?.focus(); + } +} diff --git a/jslib/angular/src/components/modal/modal-injector.ts b/jslib/angular/src/components/modal/modal-injector.ts new file mode 100644 index 00000000..188e5584 --- /dev/null +++ b/jslib/angular/src/components/modal/modal-injector.ts @@ -0,0 +1,10 @@ +import { InjectFlags, InjectionToken, Injector, Type } from "@angular/core"; + +export class ModalInjector implements Injector { + constructor(private _parentInjector: Injector, private _additionalTokens: WeakMap) {} + + get(token: Type | InjectionToken, notFoundValue?: T, flags?: InjectFlags): T; + get(token: any, notFoundValue?: any, flags?: any) { + return this._additionalTokens.get(token) ?? this._parentInjector.get(token, notFoundValue); + } +} diff --git a/jslib/angular/src/components/modal/modal.ref.ts b/jslib/angular/src/components/modal/modal.ref.ts new file mode 100644 index 00000000..a80acebb --- /dev/null +++ b/jslib/angular/src/components/modal/modal.ref.ts @@ -0,0 +1,50 @@ +import { Observable, Subject } from "rxjs"; +import { first } from "rxjs/operators"; + +export class ModalRef { + onCreated: Observable; // Modal added to the DOM. + onClose: Observable; // Initiated close. + onClosed: Observable; // Modal was closed (Remove element from DOM) + onShow: Observable; // Start showing modal + onShown: Observable; // Modal is fully visible + + private readonly _onCreated = new Subject(); + private readonly _onClose = new Subject(); + private readonly _onClosed = new Subject(); + private readonly _onShow = new Subject(); + private readonly _onShown = new Subject(); + private lastResult: any; + + constructor() { + this.onCreated = this._onCreated.asObservable(); + this.onClose = this._onClose.asObservable(); + this.onClosed = this._onClosed.asObservable(); + this.onShow = this._onShow.asObservable(); + this.onShown = this._onShow.asObservable(); + } + + show() { + this._onShow.next(); + } + + shown() { + this._onShown.next(); + } + + close(result?: any) { + this.lastResult = result; + this._onClose.next(result); + } + + closed() { + this._onClosed.next(this.lastResult); + } + + created(el: HTMLElement) { + this._onCreated.next(el); + } + + onClosedPromise(): Promise { + return this.onClosed.pipe(first()).toPromise(); + } +} diff --git a/jslib/angular/src/components/password-reprompt.component.ts b/jslib/angular/src/components/password-reprompt.component.ts new file mode 100644 index 00000000..03b40802 --- /dev/null +++ b/jslib/angular/src/components/password-reprompt.component.ts @@ -0,0 +1,41 @@ +import { Directive } from "@angular/core"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; + +import { ModalRef } from "./modal/modal.ref"; + +/** + * Used to verify the user's Master Password for the "Master Password Re-prompt" feature only. + * See UserVerificationComponent for any other situation where you need to verify the user's identity. + */ +@Directive() +export class PasswordRepromptComponent { + showPassword = false; + masterPassword = ""; + + constructor( + private modalRef: ModalRef, + private cryptoService: CryptoService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService + ) {} + + togglePassword() { + this.showPassword = !this.showPassword; + } + + async submit() { + if (!(await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, null))) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("errorOccurred"), + this.i18nService.t("invalidMasterPassword") + ); + return; + } + + this.modalRef.close(true); + } +} diff --git a/jslib/angular/src/components/toastr.component.ts b/jslib/angular/src/components/toastr.component.ts new file mode 100644 index 00000000..0e4ea4ea --- /dev/null +++ b/jslib/angular/src/components/toastr.component.ts @@ -0,0 +1,95 @@ +import { animate, state, style, transition, trigger } from "@angular/animations"; +import { CommonModule } from "@angular/common"; +import { Component, ModuleWithProviders, NgModule } from "@angular/core"; +import { + DefaultNoComponentGlobalConfig, + GlobalConfig, + Toast as BaseToast, + ToastPackage, + ToastrService, + TOAST_CONFIG, +} from "ngx-toastr"; + +@Component({ + selector: "[toast-component2]", + template: ` + +
+ +
+
+
+ {{ title }} [{{ duplicatesCount + 1 }}] +
+
+
+ {{ message }} +
+
+
+
+
+ `, + animations: [ + trigger("flyInOut", [ + state("inactive", style({ opacity: 0 })), + state("active", style({ opacity: 1 })), + state("removed", style({ opacity: 0 })), + transition("inactive => active", animate("{{ easeTime }}ms {{ easing }}")), + transition("active => removed", animate("{{ easeTime }}ms {{ easing }}")), + ]), + ], + preserveWhitespaces: false, +}) +export class BitwardenToast extends BaseToast { + constructor(protected toastrService: ToastrService, public toastPackage: ToastPackage) { + super(toastrService, toastPackage); + } +} + +export const BitwardenToastGlobalConfig: GlobalConfig = { + ...DefaultNoComponentGlobalConfig, + toastComponent: BitwardenToast, +}; + +@NgModule({ + imports: [CommonModule], + declarations: [BitwardenToast], + exports: [BitwardenToast], +}) +export class BitwardenToastModule { + static forRoot(config: Partial = {}): ModuleWithProviders { + return { + ngModule: BitwardenToastModule, + providers: [ + { + provide: TOAST_CONFIG, + useValue: { + default: BitwardenToastGlobalConfig, + config: config, + }, + }, + ], + }; + } +} diff --git a/jslib/angular/src/components/user-verification.component.html b/jslib/angular/src/components/user-verification.component.html new file mode 100644 index 00000000..b90c6074 --- /dev/null +++ b/jslib/angular/src/components/user-verification.component.html @@ -0,0 +1,46 @@ + + + + {{ "confirmIdentity" | i18n }} + + +
+ + + + + {{ "codeSent" | i18n }} + +
+ +
+ + + {{ "confirmIdentity" | i18n }} +
+
diff --git a/jslib/angular/src/components/user-verification.component.ts b/jslib/angular/src/components/user-verification.component.ts new file mode 100644 index 00000000..5504796e --- /dev/null +++ b/jslib/angular/src/components/user-verification.component.ts @@ -0,0 +1,96 @@ +import { animate, style, transition, trigger } from "@angular/animations"; +import { Component, OnInit } from "@angular/core"; +import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from "@angular/forms"; + +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { UserVerificationService } from "jslib-common/abstractions/userVerification.service"; +import { VerificationType } from "jslib-common/enums/verificationType"; +import { Verification } from "jslib-common/types/verification"; + +/** + * Used for general-purpose user verification throughout the app. + * Collects the user's master password, or if they are using Key Connector, prompts for an OTP via email. + * This is exposed to the parent component via the ControlValueAccessor interface (e.g. bind it to a FormControl). + * Use UserVerificationService to verify the user's input. + */ +@Component({ + selector: "app-user-verification", + templateUrl: "user-verification.component.html", + providers: [ + { + provide: NG_VALUE_ACCESSOR, + multi: true, + useExisting: UserVerificationComponent, + }, + ], + animations: [ + trigger("sent", [ + transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]), + ]), + ], +}) +export class UserVerificationComponent implements ControlValueAccessor, OnInit { + usesKeyConnector = false; + disableRequestOTP = false; + sentCode = false; + + secret = new FormControl(""); + + private onChange: (value: Verification) => void; + + constructor( + private keyConnectorService: KeyConnectorService, + private userVerificationService: UserVerificationService + ) {} + + async ngOnInit() { + this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector(); + this.processChanges(this.secret.value); + + this.secret.valueChanges.subscribe((secret: string) => this.processChanges(secret)); + } + + async requestOTP() { + if (this.usesKeyConnector) { + this.disableRequestOTP = true; + try { + await this.userVerificationService.requestOTP(); + this.sentCode = true; + } finally { + this.disableRequestOTP = false; + } + } + } + + writeValue(obj: any): void { + this.secret.setValue(obj); + } + + registerOnChange(fn: any): void { + this.onChange = fn; + } + + registerOnTouched(fn: any): void { + // Not implemented + } + + setDisabledState?(isDisabled: boolean): void { + this.disableRequestOTP = isDisabled; + if (isDisabled) { + this.secret.disable(); + } else { + this.secret.enable(); + } + } + + private processChanges(secret: string) { + if (this.onChange == null) { + return; + } + + this.onChange({ + type: this.usesKeyConnector ? VerificationType.OTP : VerificationType.MasterPassword, + secret: secret, + }); + } +} diff --git a/jslib/angular/src/directives/a11y-title.directive.ts b/jslib/angular/src/directives/a11y-title.directive.ts new file mode 100644 index 00000000..20e53a55 --- /dev/null +++ b/jslib/angular/src/directives/a11y-title.directive.ts @@ -0,0 +1,23 @@ +import { Directive, ElementRef, Input, Renderer2 } from "@angular/core"; + +@Directive({ + selector: "[appA11yTitle]", +}) +export class A11yTitleDirective { + @Input() set appA11yTitle(title: string) { + this.title = title; + } + + private title: string; + + constructor(private el: ElementRef, private renderer: Renderer2) {} + + ngOnInit() { + if (!this.el.nativeElement.hasAttribute("title")) { + this.renderer.setAttribute(this.el.nativeElement, "title", this.title); + } + if (!this.el.nativeElement.hasAttribute("aria-label")) { + this.renderer.setAttribute(this.el.nativeElement, "aria-label", this.title); + } + } +} diff --git a/jslib/angular/src/directives/api-action.directive.ts b/jslib/angular/src/directives/api-action.directive.ts new file mode 100644 index 00000000..3d121726 --- /dev/null +++ b/jslib/angular/src/directives/api-action.directive.ts @@ -0,0 +1,49 @@ +import { Directive, ElementRef, Input, OnChanges } from "@angular/core"; + +import { LogService } from "jslib-common/abstractions/log.service"; +import { ErrorResponse } from "jslib-common/models/response/errorResponse"; + +import { ValidationService } from "../services/validation.service"; + +/** + * Provides error handling, in particular for any error returned by the server in an api call. + * Attach it to a
element and provide the name of the class property that will hold the api call promise. + * e.g. + * Any errors/rejections that occur will be intercepted and displayed as error toasts. + */ +@Directive({ + selector: "[appApiAction]", +}) +export class ApiActionDirective implements OnChanges { + @Input() appApiAction: Promise; + + constructor( + private el: ElementRef, + private validationService: ValidationService, + private logService: LogService + ) {} + + ngOnChanges(changes: any) { + if (this.appApiAction == null || this.appApiAction.then == null) { + return; + } + + this.el.nativeElement.loading = true; + + this.appApiAction.then( + (response: any) => { + this.el.nativeElement.loading = false; + }, + (e: any) => { + this.el.nativeElement.loading = false; + + if ((e as ErrorResponse).captchaRequired) { + this.logService.error("Captcha required error response: " + e.getSingleMessage()); + return; + } + this.logService?.error(`Received API exception: ${e}`); + this.validationService.showError(e); + } + ); + } +} diff --git a/jslib/angular/src/directives/autofocus.directive.ts b/jslib/angular/src/directives/autofocus.directive.ts new file mode 100644 index 00000000..5c5997fa --- /dev/null +++ b/jslib/angular/src/directives/autofocus.directive.ts @@ -0,0 +1,27 @@ +import { Directive, ElementRef, Input, NgZone } from "@angular/core"; +import { take } from "rxjs/operators"; + +import { Utils } from "jslib-common/misc/utils"; + +@Directive({ + selector: "[appAutofocus]", +}) +export class AutofocusDirective { + @Input() set appAutofocus(condition: boolean | string) { + this.autofocus = condition === "" || condition === true; + } + + private autofocus: boolean; + + constructor(private el: ElementRef, private ngZone: NgZone) {} + + ngOnInit() { + if (!Utils.isMobileBrowser && this.autofocus) { + if (this.ngZone.isStable) { + this.el.nativeElement.focus(); + } else { + this.ngZone.onStable.pipe(take(1)).subscribe(() => this.el.nativeElement.focus()); + } + } + } +} diff --git a/jslib/angular/src/directives/blur-click.directive.ts b/jslib/angular/src/directives/blur-click.directive.ts new file mode 100644 index 00000000..2fc1d367 --- /dev/null +++ b/jslib/angular/src/directives/blur-click.directive.ts @@ -0,0 +1,12 @@ +import { Directive, ElementRef, HostListener } from "@angular/core"; + +@Directive({ + selector: "[appBlurClick]", +}) +export class BlurClickDirective { + constructor(private el: ElementRef) {} + + @HostListener("click") onClick() { + this.el.nativeElement.blur(); + } +} diff --git a/jslib/angular/src/directives/box-row.directive.ts b/jslib/angular/src/directives/box-row.directive.ts new file mode 100644 index 00000000..9ae94314 --- /dev/null +++ b/jslib/angular/src/directives/box-row.directive.ts @@ -0,0 +1,59 @@ +import { Directive, ElementRef, HostListener, OnInit } from "@angular/core"; + +@Directive({ + selector: "[appBoxRow]", +}) +export class BoxRowDirective implements OnInit { + el: HTMLElement = null; + formEls: Element[]; + + constructor(elRef: ElementRef) { + this.el = elRef.nativeElement; + } + + ngOnInit(): void { + this.formEls = Array.from( + this.el.querySelectorAll('input:not([type="hidden"]), select, textarea') + ); + this.formEls.forEach((formEl) => { + formEl.addEventListener( + "focus", + () => { + this.el.classList.add("active"); + }, + false + ); + + formEl.addEventListener( + "blur", + () => { + this.el.classList.remove("active"); + }, + false + ); + }); + } + + @HostListener("click", ["$event"]) onClick(event: Event) { + const target = event.target as HTMLElement; + if ( + target !== this.el && + !target.classList.contains("progress") && + !target.classList.contains("progress-bar") + ) { + return; + } + + if (this.formEls.length > 0) { + const formEl = this.formEls[0] as HTMLElement; + if (formEl.tagName.toLowerCase() === "input") { + const inputEl = formEl as HTMLInputElement; + if (inputEl.type != null && inputEl.type.toLowerCase() === "checkbox") { + inputEl.click(); + return; + } + } + formEl.focus(); + } + } +} diff --git a/jslib/angular/src/directives/fallback-src.directive.ts b/jslib/angular/src/directives/fallback-src.directive.ts new file mode 100644 index 00000000..11bce205 --- /dev/null +++ b/jslib/angular/src/directives/fallback-src.directive.ts @@ -0,0 +1,14 @@ +import { Directive, ElementRef, HostListener, Input } from "@angular/core"; + +@Directive({ + selector: "[appFallbackSrc]", +}) +export class FallbackSrcDirective { + @Input("appFallbackSrc") appFallbackSrc: string; + + constructor(private el: ElementRef) {} + + @HostListener("error") onError() { + this.el.nativeElement.src = this.appFallbackSrc; + } +} diff --git a/jslib/angular/src/directives/stop-click.directive.ts b/jslib/angular/src/directives/stop-click.directive.ts new file mode 100644 index 00000000..0e88dde3 --- /dev/null +++ b/jslib/angular/src/directives/stop-click.directive.ts @@ -0,0 +1,10 @@ +import { Directive, HostListener } from "@angular/core"; + +@Directive({ + selector: "[appStopClick]", +}) +export class StopClickDirective { + @HostListener("click", ["$event"]) onClick($event: MouseEvent) { + $event.preventDefault(); + } +} diff --git a/jslib/angular/src/directives/stop-prop.directive.ts b/jslib/angular/src/directives/stop-prop.directive.ts new file mode 100644 index 00000000..8393e799 --- /dev/null +++ b/jslib/angular/src/directives/stop-prop.directive.ts @@ -0,0 +1,10 @@ +import { Directive, HostListener } from "@angular/core"; + +@Directive({ + selector: "[appStopProp]", +}) +export class StopPropDirective { + @HostListener("click", ["$event"]) onClick($event: MouseEvent) { + $event.stopPropagation(); + } +} diff --git a/jslib/angular/src/pipes/i18n.pipe.ts b/jslib/angular/src/pipes/i18n.pipe.ts new file mode 100644 index 00000000..8fc09434 --- /dev/null +++ b/jslib/angular/src/pipes/i18n.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +@Pipe({ + name: "i18n", +}) +export class I18nPipe implements PipeTransform { + constructor(private i18nService: I18nService) {} + + transform(id: string, p1?: string, p2?: string, p3?: string): string { + return this.i18nService.t(id, p1, p2, p3); + } +} diff --git a/jslib/angular/src/pipes/search-ciphers.pipe.ts b/jslib/angular/src/pipes/search-ciphers.pipe.ts new file mode 100644 index 00000000..e8bdd13e --- /dev/null +++ b/jslib/angular/src/pipes/search-ciphers.pipe.ts @@ -0,0 +1,41 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +import { CipherView } from "jslib-common/models/view/cipherView"; + +@Pipe({ + name: "searchCiphers", +}) +export class SearchCiphersPipe implements PipeTransform { + transform(ciphers: CipherView[], searchText: string, deleted = false): CipherView[] { + if (ciphers == null || ciphers.length === 0) { + return []; + } + + if (searchText == null || searchText.length < 2) { + return ciphers.filter((c) => { + return deleted !== c.isDeleted; + }); + } + + searchText = searchText.trim().toLowerCase(); + return ciphers.filter((c) => { + if (deleted !== c.isDeleted) { + return false; + } + if (c.name != null && c.name.toLowerCase().indexOf(searchText) > -1) { + return true; + } + if (searchText.length >= 8 && c.id.startsWith(searchText)) { + return true; + } + if (c.subTitle != null && c.subTitle.toLowerCase().indexOf(searchText) > -1) { + return true; + } + if (c.login && c.login.uri != null && c.login.uri.toLowerCase().indexOf(searchText) > -1) { + return true; + } + + return false; + }); + } +} diff --git a/jslib/angular/src/scss/bwicons/fonts/bwi-font.svg b/jslib/angular/src/scss/bwicons/fonts/bwi-font.svg new file mode 100644 index 00000000..8b37358b --- /dev/null +++ b/jslib/angular/src/scss/bwicons/fonts/bwi-font.svg @@ -0,0 +1,170 @@ + + + + + + +{ + "fontFamily": "bwi-font", + "designer": "Bitwarden, Inc.", + "designerURL": "", + "description": "Font generated by IcoMoon.", + "copyright": "Bitwarden, Inc.", + "majorVersion": 1, + "minorVersion": 0, + "version": "Version 1.0", + "fontId": "bwi-font", + "psName": "bwi-font", + "subFamily": "Regular", + "fullName": "bwi-font" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf b/jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9b1cfde3ddc312197d8c433ce8863490df35e649 GIT binary patch literal 68412 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 literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/bwicons/fonts/bwi-font.woff b/jslib/angular/src/scss/bwicons/fonts/bwi-font.woff new file mode 100644 index 0000000000000000000000000000000000000000..fdf107376ec87c416b0ded358aba216dd20b1c05 GIT binary patch literal 68488 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 d;rOdh9Xe~VQ`~TZxmO+t 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 literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/bwicons/styles/style.scss b/jslib/angular/src/scss/bwicons/styles/style.scss new file mode 100644 index 00000000..e8c05f23 --- /dev/null +++ b/jslib/angular/src/scss/bwicons/styles/style.scss @@ -0,0 +1,250 @@ +$icomoon-font-family: "bwi-font" !default; +$icomoon-font-path: "~@bitwarden/jslib-angular/src/scss/bwicons/fonts/" !default; + +// New font sheet? Update the font-face information below +@font-face { + font-family: "#{$icomoon-font-family}"; + src: url($icomoon-font-path + "bwi-font.svg") format("svg"), + url($icomoon-font-path + "bwi-font.ttf") format("truetype"), + url($icomoon-font-path + "bwi-font.woff") format("woff"), + url($icomoon-font-path + "bwi-font.woff2") format("woff2"); + font-weight: normal; + font-style: normal; + font-display: block; +} + +// Base Class +.bwi { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: "#{$icomoon-font-family}" !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + display: inline-block; + /* Better Font Rendering */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +// Fixed Width Icons +.bwi-fw { + width: calc(18em / 14); + text-align: center; +} + +// Sizing Changes +.bwi-sm { + font-size: 0.875em; +} + +.bwi-lg { + font-size: calc(4em / 3); + line-height: calc(3em / 4); + vertical-align: -15%; +} + +.bwi-2x { + font-size: 2em; +} + +.bwi-3x { + font-size: 3em; +} + +.bwi-4x { + font-size: 4em; +} + +// Spin Animations +.bwi-spin { + animation: bwi-spin 2s infinite linear; +} + +@keyframes bwi-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(359deg); + } +} + +// List Icons +.bwi-ul { + padding-left: 0; + margin-left: calc(30em / 14); + list-style-type: none; + > li { + position: relative; + } +} + +.bwi-li { + position: absolute; + left: calc(-30em / 14); + width: calc(30em / 14); + top: calc(2em / 14); + text-align: center; + &.bwi-lg { + left: calc(-30em / 14) + calc(4em / 14); + } +} + +// Rotation +.bwi-rotate-270 { + transform: rotate(270deg); +} + +// For new icons - add their glyph name and value to the map below +$icons: ( + "save-changes": "\e988", + "browser": "\e985", + "mobile": "\e986", + "cli": "\e987", + "providers": "\e983", + "vault": "\e984", + "folder-closed-f": "\e982", + "rocket": "\e9ee", + "ellipsis-h": "\e9ef", + "ellipsis-v": "\e9f0", + "safari": "\e974", + "opera": "\e975", + "firefox": "\e976", + "edge": "\e977", + "chrome": "\e978", + "star-f": "\e979", + "arrow-circle-up": "\e97a", + "arrow-circle-right": "\e97b", + "arrow-circle-left": "\e97c", + "arrow-circle-down": "\e97d", + "undo": "\e97e", + "bolt": "\e97f", + "puzzle": "\e980", + "rss": "\e973", + "dbl-angle-left": "\e970", + "dbl-angle-right": "\e971", + "hamburger": "\e972", + "bw-folder-open-f": "\e93e", + "desktop": "\e96a", + "angle-left": "\e96b", + "user": "\e900", + "user-f": "\e901", + "key": "\e902", + "share-square": "\e903", + "hashtag": "\e904", + "clone": "\e905", + "list-alt": "\e906", + "id-card": "\e907", + "credit-card": "\e908", + "globe": "\e909", + "sticky-note": "\e90a", + "folder": "\e90b", + "lock": "\e90c", + "lock-f": "\e90d", + "generate": "\e90e", + "generate-f": "\e90f", + "cog": "\e910", + "cog-f": "\e911", + "check-circle": "\e912", + "eye": "\e913", + "pencil-square": "\e914", + "bookmark": "\e915", + "files": "\e916", + "trash": "\e917", + "plus": "\e918", + "star": "\e919", + "list": "\e91a", + "angle-right": "\e91b", + "external-link": "\e91c", + "refresh": "\e91d", + "search": "\e91f", + "filter": "\e920", + "plus-circle": "\e921", + "user-circle": "\e922", + "question-circle": "\e923", + "cogs": "\e924", + "minus-circle": "\e925", + "send": "\e926", + "send-f": "\e927", + "download": "\e928", + "pencil": "\e929", + "sign-out": "\e92a", + "share": "\e92b", + "clock": "\e92c", + "angle-down": "\e92d", + "caret-down": "\e92e", + "square": "\e92f", + "collection": "\e930", + "bank": "\e931", + "shield": "\e932", + "stop": "\e933", + "plus-square": "\e934", + "save": "\e935", + "sign-in": "\e936", + "spinner": "\e937", + "dollar": "\e939", + "check": "\e93a", + "check-square": "\e93b", + "minus-square": "\e93c", + "close": "\e93d", + "share-arrow": "\e96c", + "paperclip": "\e93f", + "bitcoin": "\e940", + "cut": "\e941", + "frown": "\e942", + "folder-open": "\e943", + "bug": "\e946", + "chain-broken": "\e947", + "dashboard": "\e948", + "envelope": "\e949", + "exclamation-circle": "\e94a", + "exclamation-triangle": "\e94b", + "caret-right": "\e94c", + "file-pdf": "\e94e", + "file-text": "\e94f", + "info-circle": "\e952", + "lightbulb": "\e953", + "link": "\e954", + "linux": "\e956", + "long-arrow-right": "\e957", + "money": "\e958", + "play": "\e959", + "reddit": "\e95a", + "refresh-tab": "\e95b", + "sitemap": "\e95c", + "sliders": "\e95d", + "tag": "\e95e", + "thumb-tack": "\e95f", + "thumbs-up": "\e960", + "unlock": "\e962", + "users": "\e963", + "wrench": "\e965", + "ban": "\e967", + "camera": "\e968", + "chevron-up": "\e969", + "eye-slash": "\e96d", + "file": "\e96e", + "paste": "\e96f", + "github": "\e950", + "facebook": "\e94d", + "paypal": "\e938", + "google": "\e951", + "linkedin": "\e955", + "discourse": "\e91e", + "twitter": "\e961", + "youtube": "\e966", + "windows": "\e964", + "apple": "\e945", + "android": "\e944", + "error": "\e981", + "numbered-list": "\e989", +); + +@each $name, $glyph in $icons { + .bwi-#{$name}:before { + content: $glyph; + } +} diff --git a/jslib/angular/src/scss/icons.scss b/jslib/angular/src/scss/icons.scss new file mode 100644 index 00000000..4d2ea459 --- /dev/null +++ b/jslib/angular/src/scss/icons.scss @@ -0,0 +1,44 @@ +$card-icons-base: "~@bitwarden/jslib-angular/src/images/cards/"; +$card-icons: ( + "visa": $card-icons-base + "visa-light.png", + "amex": $card-icons-base + "amex-light.png", + "diners-club": $card-icons-base + "diners_club-light.png", + "discover": $card-icons-base + "discover-light.png", + "jcb": $card-icons-base + "jcb-light.png", + "maestro": $card-icons-base + "maestro-light.png", + "mastercard": $card-icons-base + "mastercard-light.png", + "union-pay": $card-icons-base + "union_pay-light.png", +); + +$card-icons-dark: ( + "visa": $card-icons-base + "visa-dark.png", + "amex": $card-icons-base + "amex-dark.png", + "diners-club": $card-icons-base + "diners_club-dark.png", + "discover": $card-icons-base + "discover-dark.png", + "jcb": $card-icons-base + "jcb-dark.png", + "maestro": $card-icons-base + "maestro-dark.png", + "mastercard": $card-icons-base + "mastercard-dark.png", + "union-pay": $card-icons-base + "union_pay-dark.png", +); + +.credit-card-icon { + display: block; // Resolves the parent container being slighly to big + height: 19px; + width: 24px; + background-size: contain; + background-repeat: no-repeat; +} + +@each $name, $url in $card-icons { + .card-#{$name} { + background-image: url("#{$url}"); + } +} + +@each $theme in $dark-icon-themes { + @each $name, $url in $card-icons-dark { + .#{$theme} .card-#{$name} { + background-image: url("#{$url}"); + } + } +} diff --git a/jslib/angular/src/scss/webfonts.css b/jslib/angular/src/scss/webfonts.css new file mode 100644 index 00000000..fe9bb7e1 --- /dev/null +++ b/jslib/angular/src/scss/webfonts.css @@ -0,0 +1,89 @@ +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 300; + font-display: auto; + src: url(webfonts/Open_Sans-italic-300.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 400; + font-display: auto; + src: url(webfonts/Open_Sans-italic-400.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 600; + font-display: auto; + src: url(webfonts/Open_Sans-italic-600.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 700; + font-display: auto; + src: url(webfonts/Open_Sans-italic-700.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 800; + font-display: auto; + src: url(webfonts/Open_Sans-italic-800.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 300; + font-display: auto; + src: url(webfonts/Open_Sans-normal-300.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 400; + font-display: auto; + src: url(webfonts/Open_Sans-normal-400.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 600; + font-display: auto; + src: url(webfonts/Open_Sans-normal-600.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 700; + font-display: auto; + src: url(webfonts/Open_Sans-normal-700.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 800; + font-display: auto; + src: url(webfonts/Open_Sans-normal-800.woff) format("woff"); + unicode-range: U+0-10FFFF; +} diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff new file mode 100644 index 0000000000000000000000000000000000000000..8f8e97cfe0f16ed070f535aaf7f6d2504f62d8f0 GIT binary patch literal 53108 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%0D%l~4Fp|y8x{{ZQW7Xg?Hqn!uI~$&Hm3VmF zK^!zGDamtqJtk0}4NU9yS?h)zjpM%M`P_@tX}&N&_`+_u?SO5vMOKsB;W{e9c)2{= z1jXx@Zi)_2O^+k-?UxTKnl84DI3bmrbI%I@F5hRo{DkyxuThw{qe2bqjKAF7G}J0J z<{d9L1HNR za!8qr%K7lf)^X^FCW{G_iq(4!e#rm)Bu)MH7*L_jeDem@D98b6DT1y%IVI zo%)((X7Io1za82A3-Bu_7q^kfg#)wTmlk~_EVjoexzd#_eQ*txwn58{trD;W9D6TP+wldU8 z{8);)7nrs@xk@~h%0BNQqsT740?-l?zCUQWW=TkYI<1BypQ5Opo7u?>T!$GIQA-7S z|1L5>5Xd{Yort}c@D>vk!~_y~zbrpnwN4-~?eSL8f$^he*=rZb9i}wk7!M8qR|V#{ z(z?y6X0Lvev|kB=qQQkJz~0W(WXM;-uwAVm{>8S93yQ-1Adu+vez_wn$g>UI8GxQf zE|Eq85#engI~n%gnGpb|L_9yMoKJ)`)DlA4Wc2HV(*0;+$PO=|WlJ3tX6$*09etUV z_S^ay_VX3FjF`XbS>OxRI`Ft?z>o0y`~5iTX7?uVd_N4(?>KG(WI_Tk-`Nvs0$|B# z=dCxdM~}&26S|%XAs8x@-5E3_MPIySpJVb~ciH&qH|#q1hi< zEV(!3yXzzv2yt2UwM14|U$RNl-?x06lR%JJDq4BYQ`4vjzc@%)lB?0Nu`zLQQb;^ z0`xwe%qUk$^`6fwr`#gvkW`RCMG>1)7pKprDNvei8$(!pHAp2ch@lOT#gAMwwT#&G zD_hwKu&%}5Tu&F3cv$MQYCu&?+yM zDt)Jt_ZGi0z20tc|1D@m?yMagKX{MwR?Vp{
|YJ#5C&bNMldpM)wVcAwPlgDVi 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 q-`#q|cTHAVCxCE^Y0rZGq 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 literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff new file mode 100644 index 0000000000000000000000000000000000000000..f90b99608689f033ecfd66fb65adb3706db8b92f GIT binary patch literal 53108 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%0D{sVL<;>e?UP3LW=!7X0so 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 z zt)9Lk$fQ=kIHQ$5)3AOtG3jA+f?LDR5OT8+L$MISk(m(3<;0;AM~^bXGm7yqdPWf< zJOlEG&#-dz7rpf~#7XRqrxzFp)AH%BPshOswq(MX8u<8l?g*Pmvjl(@iZE24f#Mvg zM%c_1mGX#%-L_47g*)O`fYh__PnSsjqu~?uzd=mm<|`ehkBsc5tWQGjc}D`0ip9 zoDY0XAck?InxHW@u~Dp5)nyMf$~E~}>kc9tG)`(HOWMet6xg4kG+&usNlH7LxvKKS&ALs>l)Xa6Zc6j)d0PxND(bg`%~%SE%3};d!XR!nDbc}QWe$d~g`W3|GK_qaBVn7Zjn~ZYe z@n?Y#r{PcIkyG89{s?+FoxFWY$*RN7h2U9ee8{;lql#Wfhr5~x-J^HmbhwdC-HH|&`Kc4z#p)&Nkz?tyYQg~SihA|Ok<%yr+ON85Ek30-E2_%D0NX!*%2p%&7*;eel%(A$2JZg_d6yPkV75?4i$5YfJo(*Fd_$>be z)Q(w(;aG3RbLz`ypPV?sXGomq%u-Ivr2#XCg?heqjM|5j>3CwPI+H|4u^y$X6!V=} zq{+=0AJ}5;!WsJhGrIK97jjxOvsvgXt0v;x?8fRvy^2#?;8&?niDCF{H0}WS*0hH9 zaPN_+3ARVa5{7aC__i-0`rnj7{!dG8qah&U3FZ6dj z)}l2l@M9!j!TX)T1*c)R{axgFi)1=v#To38_KqdmNL^%DB&*^qVc}sNON`->@D<4O zK%l`Zf~CFF z;OGWIHU@tgM#I5$&PtF6hXezMTnJ&`>j><7x{$CCR>k;_^k;pz5dRQ=lg$Rrvnfp4 z_9Z5{TQ;m{o%hb##m!G=AinOMUJX|u9tAaY?uuwg zn6uVB{ady{c8uX@Y;rPTPHqIy+6Jwb<>ah?j9uZU2%sik;WRS9x3CCJ<`;0?z}Q5& z-;`V!!mUiU;mP68_}0gct)D!r+k54&&BhO2=|7~&EAe$#s%Dja^lEY6c%WPC)nsBq zo2ZV$(th&G(O>3HuKc`Wv3A?!SsiD0sJT49a?+`hj;inuGYS^@8>YoF3`=usYh|Y> zF0>ukE+Zp-^c;Md8AU+2SwW?z)OzBc9z}As<$_Oza7;SgsMM^&VD>Xnax_C~DqEHH zihPmk-K8r(1TM6y)yt>8wf$iBh~h>K%epKbGT`X$qr3M91+CDVl2#hEzJ6GrXk#zf>bPwZ>YnnvxZsOP;01F=d0k5+^DCK zI-YEF@CCn8@4Eo83@es`?7@ukCK{4@pI1M`XB?5sE~1o2C{Y(_UHPA>cGh5D-)+sv ziF1o<%O}jif24GbACTI<5kLC=$0X$D$%d z$WRudZ_p5ip;o!8{0csgaELNi*zW<5O=)0)m4qN9iCn#eU~^XN9wX3?`HAd7a+86E zSR2?GtNyI6dc1tuKhx`uC$2Ai^W(z2zLys#y;uCvB>Q3{TebG-(s|hRK33YbRk!g0vc+T^x&a!t*pL8J?aYmi{*>osIuPD19`P?n6p%@a*ZS@N|Uu zbd^}PmXysV{Nq(N^j&Y*!FtF}QXVEig7M{jtl+rx2`4^SE6)c?_B(3QXQe29PEyZ7Y$JImfHnKm(fZp)Ty+h4isT2y%~+J9J9@$9bL8vHlTymk7E z%_w!%*nNj$8k~-Z!S|m0ivl0C?)>WIaaS4Eov+Tw7>#f{WrV0NDLjN6s3wv}Emt5L z7tJn2iiKfM4h3^;WImqEc5JVMobL0p=JL&kE^smILjoPbT)}o(v2gzEVR#t}0WTaa zkZxxNAgF_HzQ1eO-+U#u%a8xtT(S63Ir-#j_+%FnVMH^1RSeCM<`iPOH{)fkF{L2F zbVvM&mep(z*Diiw-H)ul>pV9e+iSQAPCx4lS4!QP(}ees9Zz`=IT$53cD?|a5777o z2%kZaXZEKoK)#PrGUh#U#PPfm!8}hOlbqTY>^fR^s1wpwuhqpY2-qzgt}k zWeXW7c`lkEYtLI5GweTcBnDsaA1ULN+GvY~Q`&-pj9P|XG3fu?SouPXy~KV=77_S% zg@G2?Gem!`to@eikE@z3?_9HK>x`WfUfVhQgSx7wWsT!|HP7zSnVp&1C$VRF>#p6{ zGcymKDjo9i!Z~|S9^Qe^cj(%wQ_tMIQk3;d*V>+Kb4p218?l*7pa}HZk2Q*5#>mhe zQJ~~BJY#$_P^m1C9F^T}_x9qv{1!`gZ0Xygg{-i5kH`PrcPQvY$N`zpoxrC9(dqz%y`K z3&h07z20~^Pciqu_=^WF2L9qsT^goTItMLoJ)&ztx5lYeO$NNxYIt{GHBPQ_4ty(n zTtT;vT&2KjY`3&*daxoiGTe&CW~Ur14~hy6$VxjjBHR{bVh}+k(NQuJH!c-vEmE5Jq=#4R=j%vjEW%5I~KLAVEcRK0${J$!U9 zT94&ZYtfcnS9hYo+zUIe?!x!lFd`FVm`qMIa}sx_(D6va@eG0seV0_4BKiReG5!x~l-8zal;IG8eLOe@TLuu+4mZoA!X;4^QF}v55M}F?w zg}~Q}ijU%eiMWtRR}JM2lpSGYjEeB|DLAE^QORX2Tt`GU5OQ@(HmO*CAZ}5HGtjZJ zTlfyXjjZf7myNx~{^SZp7jQg|htnqvn`LTw80_(R^lsUX-Ni8C`~Dv9wr?l!l1_;)2`ZP zrq1AREm&o4^9iwA)!=mW`_pRr3FQ<{_=L&*iJESWe+ZetnK)h)vK3mMG=dZk*GU1pW|f* zpnN^(vu0lflBr~JPNxskvwA(J2<5^>WMONvh~;Cal+GmS0kYrVHpJeVb}`{B7fNJ+`696EFGlC_=rFb@04K2d?4>1>T{?zihC(aMpbJ?@ zb`rtqjs{o(-Ur}^f_!X3u)5V9BB$_^+`9sewMp)dlH($taaFV03GIh=+&Wl zTd2Pao~k$^d%(yTAI88~nIM8RLXnITm}JHY*6B>JY2`AV!3I5y8NNNvO)v?1n3`b*1OM7(c&?K=JfqGa#oFW!IkQ*tu;y;m=-%}3%o*YH`y2ce1>|vxIj|`7DRKPzuhCjSJqpE7g zjH=gX;?XnUAG*G|d}O1B>5a?F&P{IBD6&C9dVXS5DJp9c92ZbjIfOrm8dn@SQh~oZ z@-K@Lj+U1n#pg*4a$HgIcmvY1EYdwrFE2;yq3(QAO)bPwj9)U z|N9;GXwGyw-Vb)x88b3+HnYvlI~>m35(kpVpuFrZaS|*KE8c5bu!4EeDYRY7(lf{u z9Z6@HkpyNcNJN;}6a3r;`|;l!-`^J)8t62q8u>omi~9wI2hE@VpY(t6{qJ`kIK?k^ zxQ4A*EAQIAU1u%|;=*U{UiN0=p4~dxo9TVFILZp0{d<-)oQm3@riwE=x17iS{*@ZB z1I-;?&c?KUd&MAzLDUyz1lx>}<{{`AMrYu3Mqbsfgy$tszo5;fKHK40jsz|wv)SV> zzSB1^BR zqtXN_RDkE9(lKFx6+lyUC7b|i*z`!(r5WRPkL=tg4jU^4hAD&-9sRy+eV;Nwnm8`| z-toJ&`r3lCNvUtK-)dd4yE9u9^bNLO3pI_Nu*`gP_nRnYCS;cPfBhKVyz^-HLF`{; z-SL-oJ^F*adr?8!T-4T`v!fWxMl%VbXYe5E;>Bc1V+N_Gu7mi@4q=ADttsLVq67zzW@)QzW$A_BdGdmHO101WvMJ2#X4jG zLT^;^m;VFf5LHjD7+zU5bnwg>%@P_lPi>OW4A=IrnluQoMF#=CVItrhl9_*o@5>d* zPEs3;?qfg2VKqU1`z-ML)z!@kF1tReQ z9v-0r0y{gc;pLr{qn3sruOAK2w=LRq_A{d0)`mMi7~7kG(01}O-gMl!7WeVOUa8Gf zXU1H@`PX80%NyLecY9y^HmNN;v`WsQn(`SA4=w2N*#`hmThzCx_lXU{FFT&xIJ{N( z^OGB&?m!UohxBexOP2Q1KCzcFvuw?obc@szsuy$NC_@R%^m8$Y=6^%{|J->hfwFU8xIOt z8?yQQW4DBAR?p~d5I^e2fN@L4=wZ!x<~yLIXJT2TD@P4J`Y|AoObtF5a(Ny{zy0)M>y=82Q2;1gUrY~uWX zYAVLf1WOzuc;5;;^j!!+P2x=|gnUCxoNoXmT#5ZgFAX&QLg5yQW0~ioLuECwU3!@Q zRwq2#H#H5P|M$BG<|FqHTYg`>F$#R$hplra`)|8!b*%=5+^653$m`8bs~cOI_TvXz zxebgMXPW>$6wWk)7`||8V7QMdAMAe?Rk+S#Arwe~&qZ5;(W73ttt6iq(c&F>?BB7M zZDS4uk>jR$F z&1j#Knnibxo8ngRf52BRyG))z>x5^R%KA*n-u$n$oDqx{H|O(glJC@yQ9?`&%&-k` zw`vMrN`HM|T$~AzKLW!6E;5i$a+n+iC0di|1xy?UF$_}7++bO^1&}3N*OHg8{+TW< z--jvPQ;HXVbogBPk-t~nD`~ND!hddkj6#oW8(SV*>>Rmw+{n>`#s;T6h&6WVSaotn z9~sjid`F*uem#!I<8Nzr_3nZUE~G)<-0!n3B<@Q*@94_S)sr8dE=tMo@)=tG)!aGI zvVjaE&lhoQe0}5!vqh^!I+KMV5$Uk8rWvT$A(?HSoq7G#7QmZ@V5`LhErU+3jfDn3*xcCZ3Sx zuLY&JU5SrE2Yf6FokFMLDe9w9G?-H+YXjr<<_XvdPMg3BR-e_n27ijr<0TL9-8f`Q z{w)O+#Nu5kgMYg3YBqfP?y(c60-VBQzVDGg{y2hCLPnDjGILBipD;Loya^u!L#7x7%6WwpisWUSX9!^i{;5Uz#1Hn2 z`(f#^eE~ggIc~?_iASN=ONaSw8#=L=TZV51yfbg~_FfxF*x=TE7cUAScms=a^B6=k zAB(`o69qFdP!7(}0`QcE1O{<|MjPZ{+IS;i8W_D&AE~rnpdK{;%qtnvMkb`}+$LO? z&Su5B4l36#?fPC>+0LQ=#dj3=iI@(_bVk#W@1`Y`BibTFy zsbVr@6v_d}4;qaHdfj<`AQMR>4wR8(CrEixD0K>p)=WUFm@p|4)mvhjS8%hqUh21k zt2_P;f0ITtUduwk$JWitX*{gepmhz=Y6tGikLus7vg;i4*C&gFn8xq_&e&_V&7ZnI zEUtW#E$&z@!&1D64HVzjL_D>6ncRmnaqUVV4PVZwkcxl0TR$} z?M@MSja_(UUJ+jWm}`aDt2c_fr0JDL6wrJeA<83gxa{I%lM}Z$Oq=u#(Q6ko4EKm; z0_jPeTD1?a)GPCVp2$besx3acdSIt?f|1HNp<)w3MCZ~!y_Vgo^wN-QT=zRWdu^E3 zIf^fY@aWSxg?vMEqmGOlep*B;SqCjNF@YpE!KzZ5IE6+5xGIDwP|YEkfg_p1*5qes zCG}e>OcJrhLWB95D|3tR(mFH|AFjELQf^nFnWzZySAH(;(kx8L2e%wS+791zQn1Ce z5AO7CnKkV*h9$QM*)f`Vd`VSd`R~T|%t-!lccDGk}Y)1E5cEg+5rAQ-YpeqPBj}6v&a#m142(RKHDLX=? zl4}&4C5YhwkS00$h(0To7+l0LfkAyHH#h+4ld(YA{%Ti|`dHpJ%K4CZ@=U}xFL zrbg3kt7?I7&F}MkI}L1hj_ptT^yKVT;%0Q3^sA}s67WTwLnkkhel4oRDPq6U+w4^6 z*Ko#8WDgDi*@5)hMrGGg*)1A7q=@KVF1t7dU)Ig2|Hlc5_^~htRN=gU{zd;ChBk?+H}OkINFuO#02I5*F?LLCVZf-T}dF5;5Ii%RAD3x(qI2ISa6QR zo1W+@GP!^pA@~LD3I)ArN2NXww&H|D7+_dv!3s|jPbLLlnfWfj7ksvEcx864Uag0< zZaI4A{v{urL)yz5Jb_sDeiB_Ve`tLB&YcsJVq$_K+7(vL5^-5Rm~r|>m?5!AdzsAs zSv1ej8nt3sM97qJqxv44K8b*|jPuX`AYe?3k~WdrYGt$Dp&vretiwgzMOi7+hH0DS zh{*`gZqp_^uOy>Q8>iYCpIhRL(5rp@!I+@QGpU0h0yz)zZ)s!~>4&(aa^Dz(+aJ}MXUlInMvlUl;;PPAjmq}ta%%$h$crsslJ^7>C|ofsb)9a+%q z#EiirKsAx>w!|Dl8jUps!Xp&24&c z_`)EC|G3T-3yVXHiE#yL$36$oPkps|akx3YZL{i~PoKK+v{^YCOsy{>avi*$At8oC z3<$+1peXc#MmQ4!h1(}wKDG4n>5Z0A4}l($^e4Cf+Lrp<#q$ZWx*YHPmEzLKYfRRt>C;q0+^4E6_n{GPD=iNMQkNWTskxtBQrP758(&dTLL2!kh*LoQu{XMgy z1O#qLIunr&gyWrW;r9;zJRf-IkNn`e+;^oN2l0b`=({W$UF*Xh_!4i~_RDc{%e>)= zeQ!=se8mK|ZuuL@qsEJt!-cYAlygI4H5fUAQK4y9qIhODB!GWTlgBbZHj1RI07qN? z-+hO?R@ACZLUKlrEY~Akx8T=&AM|+XDwaPzGt+w5II!x;Er1ECUeSy4#P($v$cHXv z{77xkPMx1cz_*lY6CqfWy{qhd$ElJj65GC&*`p%AMT1Ctq=-6M|GEJG>P zucjJzXtLAmuDWsL7m}M4OVGj1AKxa`H}beB1W)$RLc&?6PW-eo@ton7ZTWz2dtKc1rWaoSb+f9 zVl^9hqJL3ENZ4})<1EQ7mJ8m0mZle{3Sj|81pf!vMZJSww^-`Wk{wuuzx~|MCcSwk zd;FXCQS4DePV@9E_Ru#jOMHgU<_jkhi~CwPUL4pjksJRzp7>{AdTQ&Y4N&8Ix6tgr zg3?klo5td=>vqLPC7_9jMZsO#6(-}^PZl4F%A?kih5ST*7vm4DNXZI{jSI5{>;1q1 zR$}sV#Hlzg4xm9fKRrk8RqpUa`csOC*0b)`P|u>fHN>dtkbAU*M2;BwiFH@>t(?i} z+&f#xe7{iNGNo}-`8%6_PiQolzJ1%&wgn}LsUFwh<4N@92IopzvPa}DX{*9c zSzZVtEM<8iM~+hhhlO^Vq+2uWH7~g|>xusFzAeiq+iV5n2X=4VBWSJp?Xel7Dn_@& z_vS8AR++L1SA``ojy^-Kxvp!%B^%$vx+fD_r6TiVq(V*U_A#bLY6(JXu976U#Jb+A|fOFj>%X85(_F^M-9pnmeu~9&)ly zav(Vin)t9Zh*8B^t-MdD-UpJ0?LPVd3vU5u4F3YgPs)<7|K{uCaqiqi5>F!_PkK43 zsRxF1n$Z(df|Z;8_lS_kxa%thPn`ep@maqvpER(bO-|*8-J`0@supaY)~Y0VLiM{J z%>Mh|dSlRHRK=?;@$YjMlQFPHHciQFZ|>ar%DFSBcvHsac>L9me^ykWv`6RTHfEeU zejUHJRsQ*^hz1d(=FEzUi^7k3R{3>_%!H0t2ZhHpY|F61>{Nuod5{B{7ZEa~@l|Do z+8~!DCN0etqiqz^2!J?a_^cFL3NSR-USyNef0LACObfhvCM}K5M1|d97r&kV*R~q@ zXx}l3LF;l71K~SP!|wk-G~Fn|Qa2BRf16WX*Z)tAI1DRTaUg=hm)b8vZ5E5&t_6N> zl?HLX5XqV63tbcpU`+vLtwjX$gKjtzXH7E*R3w?tb;XM3vzFQlr!o`<6h#EraYIhbhV_qu*8zUbBTLBoE)%HFX#Mr40%@ ziEreg;5}7)P*^_x1#MeDxfY+yN9nbbYf))!Wi3i;i;u$#2BA!T8lMkQS_a6WVi_ML zi9@7OQlgj4=JrFg+MsUnjoY+AyR4;+8ntS}-zalNrRVf&6y3alWuT-|{u;!z{>k_; z#@&7vootGQvZ;NUB&fqqH^{%;N%j&gOZ3~C35z=pPZ%@?C_0N8^q;h-TZm0onI0B2 zq^~{7Y$&kD*;g}&9SZSNTg@q@QpY93|2eYe`E(fi!F?7LH3kj z*i-TuB@>H}Kt#wwM#BVxtvQv+W(t{JOgXb?JMTkIIlW4k=fjwNBN{gjY8V~LH6d^6 z;LRqnrGo}_?%lRkGp?iiy^d{K6cjky^eZdkhLQII;k{wKb-ht+?>23E$8wBs7QEvN zkXt0TEg|_#oPmZ6^wh&zOrn~A*Kgz(`fHK^Z*8Z$5J`vge36Rky|VpZ{mtu7-0hyD z6dh#E+q6%TwevaAW3=4;Le`(%1TQ=_^DNGu)V{>feOeh2#xE^XK_rt#5%J?I;Q89K66 zib~l9M#1PP4_ge=m~k?h%%H4R6|ty;e=q+E|M~uW(;x4dBz%8%9Ku-P|2>6& zaQa&1q$8ck>E{RY*?}CXl&J=RX~1cP_lQ%O+$T8)c?Vuw;iWSbDd1%a5FI*w3^|c~ zQVi^9ZSLnSuTFIp`k2~=^=V>cZDDUj_ca9f2&)TVo%=es**+o85Y<=TdU63D1uy!t zuRZx*+v-qSr%K5C{ z1$fa{pYIwaR!{9axEy>f;~5iUCpZu$BX96!e3b;#(8q4q1*70_D~UACO!^_|M9-v5 zcVI}S2Rtf#o9tjKaUh#_%%&$~>cigvTkn3|XYVF75P(xNqJKWK{ebFAe)Hf8s~bl3 zNswzLTFn0a>$~ieYQ^}E-m94vWJ{S|G=QNw<&%N^$iW0Lam2D{v;+YHNlbo8WLB6u zNT-W1SS$ns=viX(1?N0UO5X@Z#q0pu+=b8xh*?_R+Kd-aB7wRjv4Z z+LGwW+&VD`RknHXNIwxA=+z7J(8aoeu48E0!T-EH|CLQ_9=hlbPwm#vjpS&fsM0}T ziwY#35x9pd6ugQ!M|3(~#d-KfG9@=+LOt(@=y>qMW+cz?iFdfLv)Hjlr6~g<6C z61m*h%J_nF#McY9#S0?i#qJSL=Lj2zLhSg)i1JDA?-}#m@_onr7Ob$ZNWN_=DYxzz zHoluIw$}BSct^JNUc06kta!C|pIk5xNLe+wOKzUK_mwv^8PA(}rCIHx z1hhp)iBue*KC6vBLC~w5BrfRBRAMPqU$3w#9C86CoA#g+R;<{IOCPp=yI@H5h|yq& zMD0oUl@%nUFKN+t+@$e*6#i!Vytb?SX}+85sd{Q|b^Eu<`-`j{9jVUYV47!zD`DM) ztK_^A09A}ZS~yx6e*!r}{7gBLpGjIqhBP9X#295}seqK}ismo@_=r{1kEp1ilyYJ7 z*S-5@t7SRy5qpZ2KK@03EesSqtNL|W*SlM%h?Yy5=XBx+p55~ep3!}7-Xi6;X@hWo zd5Z&I9bS*0Oz$(4pPM(eBSN!JF$^Mj?R+K0u>`yxx!MO1H#r|4U!~EPg*`y(Nog9w zXg|x&|TF6#>&Q_7~;U5RMXhQiKmSX|wg5zY2 zMW%%40-bmH$(~uCTteT4Q40sK^MS}A9jP7oQEzl&7rJ~5-`R!JDS|T=*d0U2-lYQb z5d*{TBjb%SB}rH@g00IK{9+6n(!1i^Wdz6{M~YkPXA`=`RJmCp`(_p2Mw+$wo8xOo zS52LAY}k6XOzqlCTH08TFX3x=&9Q{@DK!YK+Pa@2(x|yD(BfbslfNl2kkFh3noXel z04$h}z#Z~IFOn(hSxBtFY(}zg-OGzxITwFeUDUVyot-24WyBTVyT>k$#YGwIom)=t zJBJUiO}Lnb(6`mI^tgyGJcfTT{zK5eaXCf*wh&$e&Lr3K3}YeS-$skk*G}P8CFt50 z;eb=Lx|d*q|9u>=d%L_04*VX$@0B3I0SgjO7O@~(W!WQRWHD_^R5Dq#LKkfbi7+}D zXHE&AI)}yuG>&VWQxf-#phSxO@tlSK1N%_`r4zi1FT#yPaW6uDEbh|2pz>;$1@92} z#{thnM)Cun!+;z+ImERKjW=H0M_?emAtQ-pOpoIU3OXB&))LDYHw?|C7Be-DCwP(Y z{t@y0juNhP5#8m;TJ+Isl^WRAjGSG@z-)F8lmx;UMF&R$2V~q2%DcWbv*?ng*{G7ay``?4_mtrgxp_5Vs2TEeO-1WBqnc9JMbfl2sBj)Ca^*0Hasq6o-bb)H z8f6B#L9SEstcmy0_#hRl(;_z{WBr=#CZS5F4Ji;6!+{b3pRr;{>*}zdks2#CC=J)G zj-1vmgEVnC%35&G^|%Fiq|EzJ*)TVvd^yEnPl9Lzy@}&xYMnw!uDn~7hm7 z)+hzWizlg7Y`d_rI)$8U()djLxNgbV`?qT1YV7wT*2b;PXQ!ed*kI@PbzR^KPQUx^ zX}p0w>M9d4C}+@Ev-m83g&*VBwuEOGR;xA`d9#LvLDrjT+cQap&z6%(R4O<@okLU( zWZwsHRSpysg@0`o6C+2zB0Ua4cKrDK%vT7Sm2BXaE$`vu+h0SEaFA;;y3`xb;*Ys` zD=@|Bh73SfrO|-lmmu3K$w25+=dQVDN^>05{L@gw}@m^;Vt76+R_C$Q@R znY`SxePs4hy|{*AnnOkmwOMFn;7(V)Xf-Cpxfi2E{gKR9U4!lM=Q*w~M7L&Y)qT1c zC8v3jxDv_fNNYhF0X0;o(^`}fYEWd=(pszsAWEz91Q9**3BVnm^aK*E8%0*5;0qV# zrTnyb%A1G-R|V@(!Lf29JuyAUgjR!|wZx{7^~Mh$cs7KMk0q$=&suUuL_n4E;z@5p zYSI=B=c`j{ESx;IgwuJ~LCdz)Lqla-$Knk~>mj1={Dj3;sa3dg-cI_{-&flvPpsa>}y7clp5L00KaWT?g89IhSn`kR#} zhJWx*=SBiSu&bXQe}|u5!>f-Zo=cwpborJ8YDPHqXTA=+4dZBE{D<4J_N$eRLZ*QNz<@DUTjZ=#U4O+2lSpOD{itio6{g2*zz>Q$b zjsJW_kN-<)PrrR*Hk|$f?v&tx*(|F9k4+qt0FubdE%RFp+%T|oJjOSUp_1?39Fx4(?s&x1`U$(g!EMd*FZ4cX!05~AkEIB%?<)gZ@o4GkQx16(CoOEw_9>WO8N(F zxM-e2B91z|%TJzG9v16t(W>x`VLeA4xx5&08&d5g7U#P8pnKy5Y}d_T5M`e)&5v)P!+c3(bRgu_xk8`yW}8;<;bqa5de{2z!vQkIAn?&nFYa29`@qWFi|`ED>Sg9|89mqfgNJ zfrHae+|DGGPSP-vtP-=R2jDC8B-kPyZXg=kpAr!%=lDKC#H7tvMuap-ntZ)@mbP&c zH-7m3xB++P8^(^9T}uZ4K4j6a zPNgrNvWj&+*^K?r9lnd}eEUmKS`VRgAwG}m$}oCI4CM8yg9FmhJG>sX6#gbt7eb(Z?|AfX0Hk!6+m z4E_^c`4v#W!jK6kuRHtN_r)VnHLhgS*Z^FE#!`iLFPbK<7r`;IK1cw)<#%h9-b%0F8P=Zr7FASSBI3jmyxQ8K^cPSN$r<2H4C zf2Vfzg9^?1vf5%Gvia)=!gs}HH;#U1US<{>?)trP((DEi?6xN+g1wC=Q|xUjgHbPH zZ|hZoI&B2yk`$)G3sJaDPPZ;4br8CeVamz;vv1Jt!+SxeIyG6jc+d2={F{O@a;+e_ zRW8~wzHStm7p!^X>Uj{z%aDE$nR|jj>-R?*0OFR(7!4=)Gs&SxM zbED|&HXQ1RKh-s+L_Y#5Ib9!NL@K%59Omz@4f6xNDFEhBK!DaKOs5s#$-Tr%OQ5N` zRk3{Fu{kVn^s)f5}yyXl)_Qdur z^KkxHzVP(fNgLjsUHtnvM&Rv`M}h7Y#Uv1WZ;(0`j4e!TIDGkWTr@NmICdm%9dFn% zXtj~C34}iO=i<7fsiB&z91{7R93DxavR8CMvykt}tGEnz87%wo-JHG>u_CKThs_e^yl(X1611~V;fpbwqac?QBE2bl_lsL1qf{IqBlt*Y zQ>e{GXqrNb^?t_Sa{k0s_9+t-xF;?m*3b;a9e;m2-ht1=@gqm&%^tKds?P^eVV``H zWB&1kEzEc_rZgfmJ1p@hq*^%b+GTZQd21xZaEzX6_dy`%L+{h z=7PZ{$Z6oqVTrX{7^3txa5zBib(n;GT%uS`d)5e#juyK<`9%Yy5Iopqi1@M3`uoXZ zG6IG!Xa>^22o>YJB4LEy-_!Aa(XLZ4L_8LH91j%+pdsq^>BfKWh~v{njjPan2MYd? zj1e6bJduuy*sf?ZX9jhai-WIF-|z@u2F&()8-TUsmW2BKk9JA@j=V-iYL3_Faf8JH zaBX^SaON#2xZe4VdD8H4JA`q-r|OMQwhyjB)9h#0c?=+ET>@7jWzAb7u;!t@Lf((X zB4No=P8otbj1Ie>5i(T4sPT+jeW+vH~-u(U=BX9dR%2Sn1eU61wpQJqJ6E472p5md%W&2ScGS}R&CxP-VLd} zEr`eyWM*0LvaqZ_6QJ|ANcTc-c|eXAUkU{a9JZ~horbqeEA3mpa__X!ZF7nQLmXy< zuWvtr&u;*G@L%6fUE+`X3ii0CM{qf{$5|=Mm)!~$ax14XkZ24!f@4ZJtUaS71jin- zc`*jc`hz_H-^LH>zDK@zZ++@O*>1ss_dItXWrBfOJD%^&gj2rSh*0nWgzEJcl5I%I z)h*sKPz~|ryfEv}*WEdH=H=Wldm+bo#tR8z=q#ZY9t*KoxS7T%sbH`H7{};=U5`0e z#JhiH<3TKg{(f)F>?xa$Bvp7CdO#<~XEBp_Ir;dntln)UdAyQv&I$8@T74{pOAZ-{ zrNqoxbY=^UR}m(Im&xbZ2sk|rKew9SJw3bs@K;B_=_vK2kvWL(|CMqfshp@`s!uTn#}KgnMEu2|FcEw*!jnI&z-4f(W35E$rf}8 z+9TgA50e6HU?buCanixz^Sh0EC< zXmaD|RrB16)|>_rBA4QBn9Wrn4c%uASFV?mq`e(<4S>Jy<_Q@7JkD+Yu+(AP&#kxQs^zXq{@`l-cilZ$=GApM5#(^GS$B{$Ju%m(O2E8_>4F@{M@udfa*{{u|%v z!{eg!@eVMbJapllzYc8p@4Y6`+Ydk4ckP=Q^YV%rmSEX(7pSgcCz4rt;1{tPz0|66zvG**hFUNs{J5NT*DC68VTGkOks^vmS>FLjyQRGn4D7r(!S`4)M`&&WP1&v+q&&hq;mB>xc zeDBG$<3(aG#+!N>QL91GuG0`@a--kT{EjEPY(6@zW_1)g=z18(HMet_>VxaA<2pyo z#dzoP=|cu@M(>{6K+gNQ*vMUh{UCs#Kr<$@+0+(p=ZXLbK=sRYlgtEp3_i_Wl7x}; zu==u~^BDWjR zDk*M$WV*K`iY1*8=U!r>RFaS-Lrdx;lyrv?pYbY*V7;;%Xs!3pprLEs1l=Sq;^Q;k+e`zS4nU}<;I(xB3NbK=+JU$Y~qrb!+ zUyn6{jz@Q4Ja&3!?@Ogni9QnBmL^IR7bi+2m2;1lL)-S2AWwX}mpqEml013LdalEC6jW!f&vRD%k|r=*eu#yHwb=LMO56H02_SS(4<*l28v zio`Mlvk^;JWtAMLImUXi%nI?qMEq2UR=IWs+>X}>eaV?8KMEYmf_DJgej@X!|}Ecd>MFKE=mN?GqTv zX!|aTy|9rHW>nLr&srFZvuW&6AZ5mpfm=$Y@`Fu^n!}A6jaFsU!u>?+6@^5x0|@Y| z2=C&o2Ne(E%jLU^K))Is>UWz4C2_12rqtz1JKNhh+y9VdZaIxnBH>E-Rs7cK#WY$8 zXVYkBl$Dg}{hb!k!)=YVkQ9q+Iv z=an2uDk;LL4sQmbh&?@<+SC2XKCaelw6v!*1}&#D!<5hva~bt0ijt76Q<8!6w0z6u zoDn*v$)Hfb$=){hgWnfOBGfmvyZ#V+=jjF*uP)Sz2KugA@9q|Z7DxwG=4ZX~|49^f zY6)0Uh6Gzjt-p9F6Y>milTU# zEv6l@;WX(3k%>~M*_YR`Y6Al-a|(vh$yl96XL&)*revYUWQFMMuvgtUF1B{s&FBXg zACfNk6V1QQ&P69kQ*rt>Y$t6;R~eRR4|T42#sbbkW+KOdHA86-Hv@?U+(c9)qSOPF zl|6!*soJ9q6jD?500SS~o!jib%2iwc9E)mxc)}Jw&i@kqXU5bsZcgc|lmRNojT&QFYcWKqXQYlY3CqhV zFY6!s&B38>N3~lwVC!+z35AcVd~icm(5hn}YcJQ@qFsl(cPbuh<~m~A;S=q1J7j;l z=`R$-7Ct!_8P+CucsV+Og{b4hm%Lha|$~52`c@qf851myAP@>r(JUF+IC`a z_vKs5zCwy!7x>DzzK&7^O{tjM;gz9N&^B!Q@{{-1*b9^UcHcGcjjr8BygzR*$UYzT z{Uf01JBaK+NEaSuwphS0X|ZU+!@%jc1jXUS#6XEoO8}8?H!PL zFFW>|;~#y(mb|*$F|Ou~+VMNGjvU?bDFlq6p_x-hjY0AF$k}6y=C$gSv$K7t@xYTj zd&<=eSqSOp18}~MrQ8${u@RzFsg&&?EC%^G?0))oC4M{tR0HI3)EYdaNlef8C6Ub| zUtB@%_kC}+*H>2Fjotk5mqU8**w?-4*SQn+^!s}6%(=diSE}xy!JR|9_dQrMv1|8< z>kG5HEbIOL$pGH4aCA_|k6g7NV=bP{J)pLCkkO{o>vaT2L>VNH2m<{%NG}EKJuf2? zL@?D8)thY^z-Z0;D}O9HobJ=N`$os;Id9aC-j#Xe_>M!>Q;T||ih#tv#My=T#xVsM zJ3Dp|dT`jnP?>oULu`sqZ!7e$OGFYJZIvn@GSX5|63NLz147~44v@A7_fUJW@k(w< zZwq7lOQr=~1_YM=Hn73PqaU5{uf^xR&5U)1o^i9vD-mpzUwr!hvR5iN_JtNlSWR8P z;@F5V94i#T4I02f)j%H%2xq~5^(LbQv%8ep(jVl+T|T^XuWsl| zt&uelK$x4?e-Z2iAH(u_E3-Lz^t@@()h9*71L{a^*bv1C-a8A#$H1-G9AM$hMjsY} zwoKl}B|&g6x!vF!=}@YE5DheP0olnpx=JF^ZEO41*9+xqBooIveN>700Z-l zVdTEPCa`nxatLLTKX2`@DdZBqfjjS$XGO7WoSq#N(&z*H=L!4>UpA<(U zzKijK+BLPuV(?;4|Mu_rK@Dn%bV!aq`lIx8QsjQT>+?Ma51tiRIr&+XdzMH9+a1Hm zG?eFDFSCMm+wTPufk5@BIVEw}@vJeh8NUC|<%jQ~Mkg;XESkRH*v+a}UwbzQUuB2B zhW~wX_u?0*<<>5{^6}lo))&kK+2r;$y=+7SR{` zKX|KE89Kjjp9`lj;s~6Qofeq*!?Zp=T7Vz(@n;x0Ob_1Q)UL$e^kOD{F^J1zwtlhT@sRLA?v-L~*6t2KCE$fLl6W=%9IYwOz#0H%gLHQU->bAH2{_6NlegyMQX$ zFZ0!8^VO7#6{M3vREA|bgEz1)R!id=)J zm@o3<5bG+QSPzf38-H4if1CLKw00KYZ6iwnURhYjZ47baoYT0D6FcN!>0Q7MyA0pG za8hy{DCR43d&~@JA2S^@U-Folj_*F|mt*FW-)eTXC#m)0a`lb=ot@bcjHDS>kCtq| zA0e98T_wN7vcp~C9FiTTxgeI+l_p_dkP#lXRu_aU^TYBVbPm5+6le}dPhwD#I3yhd z5=aT(r5WP#a#My_m@u%CnOMRx11l-qeRh_(L8)h~;f#7Yq#4E<7VgZT*R#qd#!4|( zb+uWqOJpZwrG(oltEGjpz)u&+yQrTc#7~^e#Ze#FpZEI-Qumz{1R{~WA2Kd+$a>>c zgDFdYx8h+Mj)(!OVRp9Fa??!15#d3qVK8W(?M(*OsA!`spD#0Ol(EXn%&hB_9>y9I zRLUwWw6J6eSVZc6%383%%=&}D3*(GcRFu&RSn6Kuvk2;i>*YS(3tTHoaO*f4HP&Y& z;01+xA1Sfw}BFkn)13|g6J!uF?yQj z?B>ojM@%zBUniBig4(yTlG&Hc%)m-!V(}jtSjkK*u~n&Otl>;7;j}cvSi>3hno>3~ zR*JErQ8OzkI~gm*SnchM1>R;gTp>CqX(Y$M`ZQeF8j@+9Rw8>diR5P>lJ$fq?HK-J z@b?VyH-8=Yn^P1P?JuhbpkH_xEhp=VPm(ORdH}NEkWbgPi!J*4gKRjhO@v3EHe|(L z0(o){xPfRse?HS3a^j%RTDtzej1ZAwI%AF$0dLpPbHD_R%y8J0T^Cn@x7QLW&0bW> zlF2u)lH!iDvxLP4Rx%^&S4ur&4QFJ%Bh4_@aAv)fO^lUdtj0z&YeIH1Rti`|im`~F zf5lNF5{-RNBdewS2@g`l`x(}|=Xtp(^ zqj?N`Sy7RZ(t>sX1sfwHrR93dRRN{E1QgTC!ZR~dCV^sFS!c{CK{#+^dlEP}sLdSl zJzX2FGi^PxVS8)U$AMyr3$5li>}iaPNxc0EIF2u-^2M-yhk8KkXm~pQ}L}b<^2Cju7d6&h&&z-(dT-de?HoG1wleaD(mF5f5$x3bt7$ zN(((6ae+Wa3atqp3|c5J5em#{6ew86LYX8KnA4A}UDhclK{(*S-w{qEV&;Gc*AWla z0Hw5)QLsm}8t6bVdSn~+Y!!HLoyj8)V-F(mU_ISFj*$Ko3_RFRIkzJAr2}}dp3dY} zYdvnVFLwX=(h+#ul6VL z-?mTca8+{$^vrVME@5+|<93d92@8cu*+xxb;`lF(_zU4lbiZ^AIgl6S;hty6i+?(&>>^acA+JbcIL65#oX6HmqlB>HeqV$S@DBw1|12-`A#m54vJ=4ddih9tpdnM~ZXkj0U6`=>5sCA0Q0`TzKvt_P#**jvL(O|HB_29{DgCgE>eES@AB<(G+ zy`d_iP5MdM30(!2#b?bz1-UMq4avn{VBHfaOyt6sST27e7dad#mk)TWyfH8D#czis zyi>t^qUE68$+%AY3HQEG%uHg{%%C%^Xqdv{Me2K{%i3cFr4`xx^7ec_vaG$iptve$ z*XPuQ?-d>w_xJW3-FAf#ze+0g2lH!6ckjwC%&(|ET{CcNZvEh3eO~=BWcyUX+TS2e zLw(9n+?_p_vkSN?ui}c^=OkQHidN(Y>jzD8>jpKoaH_gaduIZxBk}q|Q6ra3+K$+} zSAMzig3yCgLo0q5_+C6IJbB{)GcjygXtUQK2Iu&ZGIwC2AvzQ?6OW zB5XvcHKWa&Hw~>WTr}#l-8pZWxYanc_-yU|h^Om-SN%jETOU)Fpx5q#djp7@2X66Y zyRlnxkzWdARb|Dpc4QsP60@fK9#CPh7IY{FB`nKD%C?!b`Sc^Dsj;QJpr~Pa_p**T zRVI~wlbTf&irD)cO^eT6mtFb}Qw(b+V9ox7S5FQqM2+!Kz?bdg6qJobyH9(a*T?4v zJXM~UhxgzY-V&avd@o%vr0Nn`ErSqQwECT4xoFM;6&HcU-YQ<+(VU+s*i(B)(TYTi zKU|Ts>sT-wfu~R{cir@yxLvfrgS`dJ0b~u6afqWn1GF`wlJ} zAJ|jXwQ0rCEt|*7yE~#w7OvRd!QF^ED_{MWTOauKb%OLTe{XVE@v*ADP*riohR*(- zmD?j_;o_=bU9b9*&A&rE&(dufU9aW{KY}yvA~*hRPd5v>tm z8>LrI*rsLzXe3Qs0mhjMSN4ezwjX{7UI*CexDcCNi>Y zlG!i!F`3qMrBw!3z>aD*A~HzgI?hAXCb!Bf*q&`@=u8dNQQ=QocV|>#+o0{M$>QzJ zcvOd&MiKn~Z^GV7KYcxY3zF~VB!s&?Q$CCe`%p~>Om=Cv+U5Vb*BO+c{1TzS-SBH< zu8P9uYEus|E0Me5LkRr?*?+?H00001000018QZes?JZvqJoNwx2mk;8007BjNR|Kq z007l1%+>rK{lN+u2x0&N00{sB00000004N}V_;-p;OPCkf`Nf^(;xYNEL@Tt42+u@ zkN|TX0F)^PbO3nTwN?*gU1=UY@7~|NHxr{qm8uajRz!5OPxo!q?5Nmyo*LPxsLYCp zh!|1(L{&uXR*YD0BOzj4o1-tjJhwa zAM5;WI5_U+f^npSMy@&G7CF{K9N3`ynp{sVlUs-(;u_JeIhr3dx*L4HqJ6~S1Qz0l z7_e1WuxH5G1`1IxxrGRji$?f9hFmakxMsJJ3;%^&yo+q~nAk$WW;jQDm-9bCqt&6r zHM2H})wqmJ8+F$$i&e|IonXZ6hbOR0zHH5C2%BBjhJrd{B0=Q$3SejYBjDHzH(jREYE+0 z8XHU<1drUZw5jOs9`pHcZ?u~vE@@8qoVE<5it#eWf*stndR&lZ(p_U=foJ4#&ouTD z9mF8#m+ijWh|b{_wVp{Wx-7@ndF~?D*T&cIXX0u+jo*e7ZdaOrAL1P{{U9G41^v?E zb*~2RF-^`(`=gNd@{t`J9!lr$L%c@)q=1{M4OYvP{cf-Fc-xTxjpic5d+}@x=j9U!$m7{t>$l{mwZusM)%Om z`L*#$jKz#!f0ovWH747l1(al~@<^!h}zoY#t-WEjM8cs@DDy7v)&6Q9DcUBqy>j#Kdn*ABU7I!pWLZ0)1i^Es}b5|6r-cm*w*qdVvf zKIf%X*pIZ2{c85PKpWD@?F!h>ewrG`2ysO{n!FeO1S7>`#`_>nGM8R6&d>5W6Q9O{0RzchG)0HFtoXEVFdUCF zULN3!cnbsZAMlkep-*`beT}ElhnQDwwgL1<%a{z)IIiBOF%K(Z%wD>Bf%(?1S{J@Z zeH*^O1bLr5>~}~R;62fjN-{2eI&g1(rX)BgwP_x!;M3N>xI6Muigc-47*WPh&jY0s#AU#iu{Q{6TmTp|XOmuO0s(8T!7 zalgDxle-vWGi1g8K3W)yh>ojPF(&`%rjj%6x$;*z6g41g1>SX@+{#>6y-U-UEK1z3 zJ-Sa@Yq;(dc^2uKI@-4Srtj<=>9@>r=2BX*t?wstPq^(zi82|+XLpf`t4JluX~ZTPYHLEMr2vvo2tJL zd=K$`o%^+u&wBRfcs;BWu?22Y{+I7PzpKxxUa4lNzA2}xS#?ge#@7VZIn^3!)Wmn# z7WK)wWg8`{HqD?z-zm(=&Ul}^iQV`!OxX+Ev(K@ZjIz!?bSudka*Vrlw90$z>3fCx z)}q_SD*Z-!*`8bRI{aAdb^W3I=6mTMUeEuReLlaBtG`Qp{=biXt*&PJs17J+hz+8? z!oqhoM_~Mp?+^3%ebmR=Q^pAMHq)$z?WCc=Im(%+xZPjbk4>Vy-bIp3qIZz4u!RFuua07S){G!!sS#5r8MQtnX zCY@53)(z5~)w}f5^_L8Qp{Sv=VW{DX(PS)Q9BaH~(wIt^hMD%89-3Km8S^0X8uL$! z$x_`i?)M#5iM5z@g7v2Lo2`y*r0s`2yS<^kmwk}^n?vsq9nBoG91oomXHI8*XA9?4 z=T_%4m(#_&^13R!mb$jPPP%Tp-nx};hr71Bxx1%(w0o|5gGcJIdMHoIQ^Zrv)6z55 z^Vth}eO|$v$6L-j)cfA2^f`QtFYPPptM0q*d+hrHfshyCpt zg@bTTxC?v^z7KyuREP`Vkj}_ZWID0}*@c`&t|O0;uc#J$RxH#QubiLJu+U}v%W*asZI-8h9O@xpjjd?x;ekP~)-A)-WnqBK#L zXixMeh7ntdDJ!e!?TeBe`9Z`A-HS`{& zS1E#&fHdg@qzQ=9i(Fa&K>-C4LT>@7q4y3_rH0-V5$Q+=1r+&muj{_|?p^Es`_}n0 z=gjQcGqa}bS$m&zoY!~-RL*iW3~Ed2rIr7F`_0{IF$rNd6QIxYwNjA zS%^|+xNSJWw5ODaOzW!fc!MX!X^oFjF`r$pFEe_rS2>Q50a|Tmu_6J08d_2SK<*M3GGi_6Rz?}S{}Vlx@zvi! z#Wcnwr8iu#G>_R%Nh%0}4i(NJhD6&hPSr%vZw=TKBeUNz+1Xd|&l`RPq2SWpEN$Rr zYPOg+y}YY;j=rD!qi;{d%0M4-wkAfu4?nz33`1+!_T@+#YmqvZnC-K zJt~{H+fT7t=*`;{u)Lv9CK>7S&c3={Mi<1JO`j8cU65&bqolGybVy;n*vp`~OuejchEO2?#g4*RO?s(;Meg1#y0(&jR(@OU6!IKnFr4~WNUDK} zD89BI3g#{inUG@`6Y(|K0RXwRe`TuWF~5v&3=L4H zKW!h9>dt(9z+&V@4zl$9y6y??-(&v1VEj>HtRSr+<7rw*dX71d8LQ=zMe|ulIm#Wr z7q(a5o~Y=gy!v>5>+9A578Jw+%c98S*c93HHZ?G%G##p+IUBT|WAP0=4p1zO4wMRt z4HyoP529YqT2ASh>`>|O?U4Sp7%~&elfZ+Os1iHm-6FOjQz8i=tG}6tJ@RPq*c2l_ zAnuGYesj*b1pZWjF@%&9PFw5;U>>LvE zCKMr5z)+%Mfe7hU1>APvSWtb9XkBB0>YPlF)^Y6VMjO({8)WGf#cn@;o?uf*xy_48 zq{iLcAm~J&f58VJ;p*uxOi_Ob}QI7?~{}(lVW^$Kfg_^^p~vj z2m7m@N-ss%moBtIGL%K2JG8v1&5@Px@Jty+}M;5D8^Zwtp?Js^G*;1z%lU<|l{ z>+o&?L;+T~$4me;03U!Cu$Y{1LyrKD{2trBdZG-S^T6t@?vKLo;g(iVB>t^+8&IS& ze7Df$WCt4ll(TMPav3agw@X;pfVe3+WIq^Xy~45L2g(NS+txYCtmwtjfmc*sTa1}; z>KR)kNfCPwq@G&*s^3Bh$?nj!m8PQY6|`A|p12J1zOO@&N)$Z~yBxhr~dqAs`PPJh@dl3t=EjYS~JvY3z^2|zD&#h=%Gs%yJxOut~GDXV})va@-Rx}uDgsD zHV>w8HQu2|%})&vaCc%UXR#sPXvvS6dFm^d{FqGYrpn#+#WoCvHjmUj{Ol7yaP67DJK>b`wate4>w;RGcha3LjrSiY3|d^NB=wNb51hNYQ>`O^I2g1n4e zFbTXU0;d{j*a2z?mx=ty&a6LCB$cxqX91%E$6`7Aw2=Zv6{>FqW?ZLBjVd{cWT%R? zYU#=s>vpjvg0PD*x8?uO`cavdbq)*K%O`e{ytA04Dwp8~me%GGqY7`t(uZ}3X<(J} zr*JjR*ViO)qBSv`5VpgO&U3#Pa#EEq$2-ns<5pUtUBl&o`z&!S$ z`=amG^jk~Z4(25VgM`C;jDdYWG*!ZJG0xn3L)Z)_=_?brS@-!m$EDZSLEl(peAeb) z8aagcgmf(N6d5YWqNE6 zs!DXHh1fGdUhly5#N+=cOoJGGa@V7#w2kG#F;`L(VX~eP?go+IKYRTV}E{;~--M z2wli1dF=>f92hHvJ7-jesU9QDaKX8U)Su33HaYcIZNb@7$-Nv94ksYHEuh;(oQPJB zy)CHMM({F;OD9nP+CrXI<1m~ksNVsMdPO5>P^#NZnp|pJW>BWrP8gpq;WkkU{cp$E zK@NLB6|NQn0I1XJqCdsVr@ci(MR%K)Iwb*_gi1_Cm_9Hzd}v@|G>4s@oSm4Nk`|K? zmx4&%X{@TNX{fHR_4n}c^7Hid{(g9R{O#!M2Q>lZyxk34Pw)%1Q1-hQ2=3ld3U=i@ z?AH9_c!3@keDaY}`RaIyE%fRH7W2UF-MttZZi_fG&sFaGNG|hN#x9eNgGg@6*H(eY zw0y<||9vllCeXJL>UVn|uFhr!QFdqei5y?`qq~IUiZ_2z&%#=hv^#xkcvua3=)YTQ&-A*c(1*ULjd7 zdO1N(Ml{jE#;ZVd5aINheZFxqlI56VO=nQ~^yF<$DDefA6VJ9`a?69^xIDAMnDlhe+<3 zW~eYyP^T@#yR=q=8zE0X3HcVuc)gmrLo&o~NJXZw^WI;!(pA3%trdp=DQq`;?SCxR zjw}4wTkPM=J%m6h{d9n6ERlYabwemBcd2dC zt6XhMjzF={rRkoa_&#j&XqdNPpDNSTl&_+~IgMg5f0(=PheVF(<1bwjH=$pUh?YS6 z-U%+F&&VR#dv{##LkZ%=UtiAn8+&bGg9B?jehd^rO&@_bbR1e(@l5M|$1#SA{L@-# zmZ*|sDv?wyvdUsmUa;53RgkNy@10rm>Kyu7!P|enWuvrE5Xwq93N`*`{o5o!mwx5;4|vVzJ||(CMF#6 z0S34Hc{315bx*FUN4vzXGTK#2D%n%?Y30xJu+=Gec^17szJMicHMvNc_B-8?d)Vjv zVdvQgjGjM0vfLDf#RQ7mO#nk($Q^3>F$pkjKo-LOvZvHR^^TW-SFd~P2wB_n$=bXO> zR;E*yha`LSi3qg-^{v%eDSV~Q`t`A_4{5uEH_s?Nb|^|=#jBsuv_~oFT`9`aaSuN` zO_QlJh6JMBxCkFHkmlMo#k=!Q3ztHZl}1Je#_#_^STBvoZCFiI^ke3i;6D`dRcm&P#+6;L3r!fmx^^BHXBN=JS9L#4PiQ_-(>*k8Jb`PSM- zKu~i|2E7h1szV}PP?kusp#Phf>=t6wbYOVE^Hudfn0aqHC~snFe z&Xp+lW7{>I&dnwRU#n9PJ@!a}M7A2w6ngJCY?YBlgB@i5>O2N^#M?hW&T091`+7uG zdguOQU`a+gGQ3xsOCQL?{u}`oTDwcaz)j?9TyiNeZt790#=7s(yw}iU=Ox+#O-i2q zK|W?4`hz*fhd6GrYqmO^Lqb6A5g2R_b8vem8iBxkC(K`op&+eETxK>5Bgb2ae?+dA i*w?qX=-J23y~qyWij9p8=b{35bFTtKKWSFu^#2bw-LH!P literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff new file mode 100644 index 0000000000000000000000000000000000000000..8526ac3d144400da2506a490f8502133dc2169ff GIT binary patch literal 53000 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 literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff new file mode 100644 index 0000000000000000000000000000000000000000..c389a1256c33f026bcac7ed19640b16495fbe693 GIT binary patch literal 72092 zcmZU4V~}P&wDr?Arfu8PHm2>Kwr$(CZQHgnZQHhOfBoLC>i)T>vXjcHwUeCG**iQg zvLYe?AOHXWgqaEW^r!4iMUTmS$-8Ae|jDh-?+2><{>(;v>+PrdBotD@+}wkB2p0MU;= z#4-SYkhq7Lt!`rJW()vO(EtDw$p9c_aKNC*Z&M>ZLjd4u;76DBKf!@+r8NBkf3T0A zJn@f^Ak4!Ym|8iy0szkh003k?0Pu?fm_Rhz(#GJ2_x;ljNP!rSK4hN@nQmvBPzje zMieT+-(!YBjEa=6(~`?+E7AY)G6 zC}PguNa#=AuxduvJBhDvvpq-1?VaYy2ddRF zkWctg0%Jhw0{c$dJ{UA~gL(V|1TwJ04%IR+!w%js{PBtLH2@S-?i8MJQI{x3@K-5( zr_@1xuJ&0!$Y2!eTtu4^=7Ah4=`Vfkm*pQf9;bemgr|EKeQ`tQFk#I<+8 zivZ}2$_W2CpjYkW4m)=;ThAPbT4Oa8*&Bj8YbLe3=e*R2PX+8O)1x3Z3THJ>hOXqR zMHml&E`%veLqcZcxfN%pX&?jsn4)KA2F4brqmiP#a-W=pB_`aJHnHY3H!me4J`Hpu z&UJ&ug$BjP9UK7NDFn1KKQqpcj{k9?r}n}WU>#yi^OQ1T5?&pXPPbjh9@VlzdG4C! zK7cZCJ(AuDVvAGRBH40HaHAJaB|vDwVP>hFw8z89uqt z)I5WBW~^Z_>?kQmKv8%cKqT<1OUQhrammV=Yt!3hAu6m#)ZwOmPE>y(PK-RbwS4WwtUOm_quaSifAO4W)`MY@FXtBZ@P#z~n|F7o3867l#<5{~18 z%uJF|ahdp^DEwaqD+sh=qp{0?`gIwKC zBx*_DGg7@)#)7G;)QHp~>&v)`oUm_e`7QI!v>48uc+GWzyJAgI*=hlpe68|#DXs2# zeKT6x@z=kh>^N+e>!4=Lbq8bBb@&HUkCn9&++g^8wbH*_w_75U)r;2kFirP?pbe@W zjpzkwN(Gyy≶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 ~NUyoT`U8H`7_331S(OQ2b=kpri`dTjf+>x~ z^jz?O#0UMr_(5So$bBoi)SGbKgse%42C`IuWJR! ziw}`^%L$`rnB{pE$=TRpH*!$CTGq@6`@0;HrhnGT-C&L5jOHJghnQD5M(+pp!Qdv_ zng4AO$1LGA+v~$Y+N2YeVIIGY5pfiS`RxBlp6FUSkFKrrs1LfFr?DM2$z6_%?w)kC zhSB|$a&KA4bur+0b4>V))N+h?4+-9Vdtx72oeY%A>W*TI`)MAn-_jjT; zj3Ybi463}HsPZ3?XR+U#m521FY&?EC!~5w+CJE>r7N(HP^4=hU3g+OFICTyRkslN@ z|9r$fP4WI&LVGxhu5bwNnp-He6>`PSV~DmZGTmt8_*&b~y|l^->y<<~QB52-Lug^V zWtqI#5^;_9TMKcWm?wq}fH>jUyF`#SJn8mn*zWayn?Czl|_PI?x&9qaJwLtA47!8QG9_-j8#^5Wa0+$1(cz?(ibNS24FX zNeg|qCjOYdSU{U4q}`6mk(VyBzK`qV$R#r(mo=_6#-8QdCiI&tIN~_#0GeLf%RLlu zFURPk&1ea0QRmimcNJ~0CtOAjwcf!zt?^Q?es9JkZ5znyvn9y#=jEaO4(r#sr#b0V zpVydEzqehr-Q=NiQha;>uF6U_~}S3AzN zi`l0^l0mNAwG*rx-2<6t(zzIe!{I}YNk+NI*>v+g@a(?MgYquNZY`&a834>>xvX|q zn>*g>uA~lm8t`?jF+jgwZ!ZjuhMF2=O+eRf_eU>E7gOGuHp9+WF zEP`E$pmUmOl#+GKtJ+uZN&kY}_m9dwFCq7BG0Ua$Yrjywl|T4@ zkrC61H|@Q&5?UiqW%;`VEfROw z{wLN~$lHmRi4^fR@jc>P1bjC_x(twU^1Y0|GQs} zV&=*3n_aX)QWiG;8~7Lb^F+02!spCMY-hQfV}4AG5*0){@g<^*I7hroyvh0aT|QtL z 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 literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff new file mode 100644 index 0000000000000000000000000000000000000000..9f56d335b1eab0e90d1f2062ed8a2fc691501a49 GIT binary patch literal 57032 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 literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff new file mode 100644 index 0000000000000000000000000000000000000000..153a1d63a46069dce57e60f5101bbea34171956b GIT binary patch literal 55324 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%0D``%CFYu^! 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 literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff new file mode 100644 index 0000000000000000000000000000000000000000..101c0be6e4dfe5f08ab5a66799cfe8c90bdd7ccb GIT binary patch literal 58016 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 pf)QCH*%aa3RM%>9ehVxu>r0o|`soeR!sA z=l$c0eeLs&ySh)C*lEU`#r1@|a!+X_t6f%oP5lsNUcID&?cl4Z ztgNW0vlL0u>-{DPpbXO`TMlx=+(q{4#%yY0E53TPph`aVIut6o9olgHy8%kc;2{vg*!;?p0ZP=1h6?o_khKy=_mp zN4JJMAN%!tP#iA5bmtvhcFW*obQ(8A9i3eZimTMoalx==V?R#qaxB$Mw(QumWXYdi z#toK~V_nEU3BYKQzs{H;ouh+N+PS#**lwA0RCY8ilNwB&)7Yq#c4|b3Q&x~%g^FQR z9*)$eBzN!9xwHa`wNOt*=VU>1RaC*^=~3Ffd!(|gYnP5)x!>2+XoKA<&8Ihb7S$~isC1VIu<=qTru!JuK{p$ z)Zry$(Gce7;HEcj9S{F)Ws+q+4{;+(MtNvSq(?ySH{@n#r3N!Hva;MhH zKj^L0)ak}>sc6SxoS&MTn;lAnl(OvXJhuT`?S8+g7-sAg50gqTR-EUWei8MRCQj;^ zZSj+oQ$I;6pWrD;kQ`o5#)01?%OpvT=a%8_hh&%2A?hcM#HH_jO1@aUh6@j_C|X;@ zz6ZkjyJ-4tfpU<}?C4|OZrwY(AIA^iLh@kr%2`m|7l<2&Z5;>yZ5@UXrurGHZ{nrc zgiu+_rq}yyJfGwZ$bb_q3kcjODDJYHh|?&lMNHIH$$^K0LB;XI;hKLhtzYu_w_|U3 zefa(p_~}ls)RV)mSoIQW#3z@&CtjyL%CQ<@E{0U(rhK?*fU3?5`;NyQkc>4Hp~z0F zP+gOcY-CR;62>MXq?r(+!V=N)U<>EWUqp`DoHz~36p;vOgT3&=x~*AJ6N|~datW~9 zDO$B-7r2?-7K%s>n-pf3CsEGg!_KSccZmr3OvI+jeuhYh2qmRAZV^Gj+v&Tg7jSxYJZESwNb3?l&| z%MC_QbU>IEi)ciJAHq6U;sRM@c`@A?;p*%TDSIWYriZ!u#dBtKnmDcdE@OWC)IN3h zy=D{FWff$X&%U{+b7@j)#eRhG5n(?sNW+jFWk#$f9<0$ihZFt*v!4kbQxX>x@_sBz z)xxo__F7}-bf?|!50ufpR)=!QUWgK10Sx<54k|~z z;2gXOZscntbpuP=)wXMwG?YopWK2q$+|)3+VcPUDO+zP*zi#Bn@qK!9 z9#GM{cej51hRX$6X(fT&+>8z#T*HTtA1~XzW|v@KS(zUc!?!AU7FG?v6&EMGR`%Iq zB}pB|p7p9&{h|HNPON zBKD5Eu^>NOG<;(0Cx5E-J0xks@OJHTI_w)ag1s?88Q}%;eDPz3@j|m3!>BzfL*2ml zUlJ%R&TWSzoh6uA-pSc1(;~GimQp>b939k zDi+s)6$@2@feJ=Sl&AmAyLi<}&-TW6nNlHZy&|=eCsmLUOc&` z=e&NS?1P09J=5;V9%T;?FH+iVg1`FY9ufp9jG9W7JKbyAuu-<~uuIMCMWb-O`rXl^ zxCbxaUR}JubQITnAIrEW4RXG`0yz<_l6pg=z!ePm0|9VIab_@=gv>e<9F??=NrqVm ziiTiEPKPlZ^rw22awh*MK2pqJR!Y?j=tY5s0H z6!wDRuT}vYF_eHcO0;9LEI}n>s8AW9Hpq_Kx|N82g!{ZIurf-}%evFQ@YUt0+3n>g~3;`N{ z{^kpHtM8jqf71=W??oIcRI3}%Sc&>b4(ISdZ7cyf99D?%2nY}(!J92`x+9AKUq%~m zghSq_#!4hWv|SyC0IqK>T7n`w#1~P*`Q*4(NNMR(T&`3>5Yxyy7AX-Vk6*4;<0J%% z3CiL*e6;GLc245f!UGHyjyTd4$|&-0h=iRR#~vn3Zh~Qc$_-|wLCtxO69JHTMs`|~B?)Y}JhFiIUk;$OnnstKYGu$bm^y5+U!#BUvpB)8 zr1el#%Fb%_tl=8Rdd$N)L^V0B!@P8GhRsHfUd*?ZZy5dVXDgbXo>Kbw%n97&TkVc< z&ky#Fbv^u`hjf4Z&={ zGD22fmT82Jg=RRz`Y;-GW-|x78n2^6*Cuug>xc?ttzowa1EWt<{C)DlQt${8vX(@* zazh9=MnO*G-x8Xx@zmHtIK$OB;N!1{IcF^P1p0hAf#C#dDRR4MzF0Vvw~zsL1cAtcpoMtmZY2a0GI%$<1gVk@XZS?zL zIeK9xTAgm$AQ);J1ufrE>p`0qe9C(CZaK7MEZ-DfQXMagjupgWP{IJxH2aBn?yqa)8X~;+PgGhr>j& z+j-c5J#G$TS_0W@GI?k*ZqN*@FyX9jn*^>L9wF>-JIrCN%3Alhk_&7i(4$oAMXP#i z_R7SHYjk!fzg$&z4G+d$$??sx%4_nn#M*1bK~GPu{u=Gkba|N2#CkL_QYuTZPdFsr zVxdsZ9tauqLMJ^idmz_|mn0iQ{ zn=qZdDuGe0 zh#zIJf-(q3WIF^+tjEctSM0`JSQ(8yCsy1{zN8YIhr291ON(h>!*)DoZuF3HYKU1N zPZt^)DKtUUmrk&`u){$M%0M8I2i~$70eqK(I+L=C7Q_?~;v5d6fn!^U7UdY{3`%gb zLn0u%ZK`@x32<1!gvq2E5E6!{)tMGo6r>fRVa$# z1OLj9874$ZEC37ckaQ*(ei-y5y-O#+CWv9fhEE(0!0NZT+_DFG>KNB%E7kBcj$=Mf zjbROVEVl2_U*hyVxg>#e6(ZOsEXI|U{ zVXGPgd0JiPgCb!`FLDOJ^0(TR@;G)x_i0GA;y9!;IYfk@fF0sB_}@eUGd95)r=?y1 zS8)smo!q>6cv-w$50gF1=_E}M>(0ylRL-EZ2r?lv%><*M5`rXIkwxTaF%i%iI8j8h z!OdHdiPphj`njBRClr3F<*g2=|SXseOu>DDYljbDRoGGUMpuj6?+ zF1#Q~W96Ds;iau)Pm4sjdCTd{Xe|%mz{Ye;3)3=fm`21}DFTjcFGUplXXI0!81{(YkBt=*CE)*@@`II@Rfvqzv#W1tq7` zW8Cfc585P|+W_PXQ zHa+~?44daC->3|HA0CS5<0U@Fk10g>>ax)9U-SR|#f4AL&YN_{AB#G^uK4433zy&y zoQF!#Ly_7HDw_`Z9kR0Q`AJ@HN`5|wr!2*8FGcxT@P7N^90+8~$+5Uxezc*5R!)qNe#D5DvTXb+9YUOSghH#<&{C@rR5&D69gtJ5EtHZ;dDcN za$e_1?U0oVM%{M~{15iu^X7KwIjrOK3Dc+eOnE7ro?Elyr*;Ly`;EuVx9usF?H_v^ zGH?afVHYm-IKT95+P!A8FkoT-=U;yPxysr_cMXlOinqkYD&A(JY(*dDDwX%9}et_pV1AGXV0<%%p$+Sebh+68&q8J7xRw7fc+Vi;Y*&*-& znI#M)vy}Z`scBB{@MJoxIAsJJqZCOLc^<&BFg6=ZoI$O&qp7^wvoutwghGBTb*Qj| zoL6g6MaR+2U9CrjkU$s)Z)c>UPqE5c7FJudhVmgk%^Hxw8EjrGP1MC9OJ#q8W zI?23$YBUaa2F{gwJaD@|&1}ld*n#Uwf8xU5!(!I;dAQ5Yk@%vrx2k*~8)}NQH%eAc zZ?Pa9&m&~RnByFvW7%b>yg+SxR+yp!2DbFr%Ijm*b+x&6b-;}FQoTjSi3SHbsJ{ek z1#ow=7sAH&!p{I6$d3RY+oD`TC?W&q()cCu(s9u&5*{)Z1i0v04X}mMm^Kew6y1MkHd z1QOGOj&C%BvJa|*!6xhM^gd^^(yHe=vw^pXY|g4Tx_G^`P+dOLvy{bQ*i(wWv7rTm;|AsDsRGqAe&?hOhdcH#=$zF1wO4ZZ{jsO zMO?|acphx~688+n{KhXPC-G`rME8zn9idyt6gY|(s@n%WZKje2mJQ=YNhku9kWD6^ zJEkuu=^ZrVLG+kgZ;tTLYios9m&JC90V;vkwXt`rGpkjJY^E*FgSoW>B;!F|$`=L@ zJuqYF!Lg&)dmSJTDDxzHtgQNs+Z4JsUx|g{5*Wm4wKyi9SjD&lTFp2`PO-Xiutr;I zh5M+b1wxRFKA+A;tC$6Jq6pDO79$5coX?9mPI5WywCsd(%heKP63b&L+{v1_yzqg_ zrzG1$+{~ODCaw>f zE?NI84z&TLzq>UK!K6{`D|roa#g{ne*Ei9!dlP%k$HN6GFpwffKiP7X;(O)J3kb)m z+#@H-kGSb>%TbG|T5H|lbReE?WUb4r!BSTWqQXO!FnN1$H!ffKw;MkCiX0v_d%*Lz zkq_XeakwP>-tOo|PM9<~Juf;_wV%+8VmqxYh`^W6Zaoe&%pbE^)>)D4R{Nj$M0k#( zu4_-`S1x(vMQUD}v~wtQc;k{lmKXs7pyed0>oR| zX`n-HdM3*7LsC(e%@Z+t;C^A$8*`Z zg`GhT@GK02A}G6E(11k|yJLarrP;!$2XMb>^T`)m$v$#VO7~}M)#)JCfPt&YHdN^QT|=!}bkI zU&WE2qo$%P*!RmabUNgACx@)E9QIRS_iT$UGpG+5(;P;s=pe`)%kV{z3t^sjc=)us zMqVw6OZ)17pUpi@32nQ-n3axmYsI}-m{&NRbsYMgr9|Q5`XKh4f-^tr6IU&n^runv zZ{0_tvik35&7EX7y{#(OV;emF6ki z1&&8fAvKT@fVhzi8tHb8v29)WGd^!wjUc+_0GF^SK$n9$)chO2l2iV+NB9jo3#R_< zE^=alGS<2r+y`6Q7-l*LBHi?4j5Iqo-efu(ZxA@TC(<)5Jv}SSg;T&~FwrO;Lp^P~piji}CiF7I{ z>sE0?I{ze#62Y^Do%az;MsMak#C0>4xuMvos)fpG+o^iAAWA}p(?hg)MMO)FV2qH* zCByZ^Y60VVEd+gtsHK7|N>T-#7}%mQo5eysT9>ePf+anF1v%YDC2OUuY^xf9mp7>M zc^s2w&q#)3bJJPiV^|GI6r%y0^&X1RKxY6L{xt(y8tvEsPN+cPqXWGc(3Kvs;Oyy21&UaGIuawZ+wiw53#G$H&;jqb08|e$(%^$c$U2Oc&zP&0qhGe4CVxrB076 z$4|#B(>vAM9aN3iEYs?s{@2E7Z9p{!*@!0Er&%DGU)F@{Oc4}>sUE5NP>5YWx*k05%lE(2oy^bIsTujvK2!`tjAI5^VI}-x9APq=DOs^AqnO2-8~#)22O?7qC{-D~LV`yWQ#)!~j zq0FyOd6OPYyZwZDWW(&}Fj_rCXi|gQb{5@@U6+@{s$lmMni$R&#CaGE-hkK1+eFwX zQd_2fie&X##ef4`v8^|>SULP_vd%WOYm~-!ND zmGd1b8UUwNXl#H?(-Ev*XHTVGXD*_jE43mu3Joy|Xd-H0d5b(3VkBZTI!i)~2AX&o zu@)WUD@Dp#%-#pWRsKRE!fg2cS@!#F2njtHpE)X@7RD#GCSp}lK5G;{Qy8CW9Z3#t z1d33HgXPP@tWSbjZbIrZS3SGm81?TI4&P9NP1p4%)5z2CH5)I% zOKRUa)EwIN93eQ0a8h=+r}-5TmnK;pQWSN;j=ybtSZzB<^Cd zkvWpjhbi>51&-N(#V^6T{h`E_i8n!VtjQ_z%!4p*U5%DQ!m@fdRA))RwLild1dD23q4l1C(!RE;ch05bdX`xX;z)K~Y zuP>?dwMdz--h_(9biSe+l==EeOZ{q%0^{Q)m5-u?JbsOaB&alik53dn+9mQK&(ip) zMY~lh*{!GqHBM>=Bjq_76=v6Eyu7TP&8>N1mQ^N`V+t=Jg-VXLH-e5P41v8-j|wA6 z*ckKt>6s@%$5Yflsl~mB+l=cFhD6sYwsLXT71YeMshHP)I=@k9x|%v*>}U?fwyA4w z0e&MXP>kK08&POd@r0MK{=cf8A7$#yDu~}ZqypCe53*KM(Hm{FLc}KSfUakwP6{o4DYUY_j9t60 ze6`c%LYOv0(9!@cO+pj29InvgzR+4WLrYo5TB-;e4J`lQ=17r^n(we6h`cEA9B0={ zf))G=PU^A}%p%0580^RnN)AGt)giK&wZxFM7DJ`OdsUGZ23CgQA9w7wZegap|{A)W%bTxChO`=>I5E521EA~V{w3cN-qCQo#An@Y9D8)XPTd&XBIu z#fJAY{S3Gs#a4p@l}7vm-BDc@gq*yQ>I6DbXQwMdAcLCFZ* z5vR_>WuyVj{Kp3FT>0oz6Y!zGe!~@k3uH+nWN;vL51c8vI0;oFRa2Dq04D*MA=gE6 zVTuXa*$8~%ww!c?!XBR?I408*dmg3h(-#-?*a7uUGXL zHxuSV!TyixyLPYp4sV+{W#qUXonw7DB)L#JYDcjpQ}vm=74F77_{Wmk=ka`j8P-rB zRgWYohi0b+91sYEP*BZLg)X!N$7r6+R)7s+hMLe`k{t&VT-MUBFdS^mGzTAXO}~*} zPVPTE$sk*C(ZFFt`{IJ{zPWu(xAq5yPah{tnt5oH3+G@Tw&C)P*$?O8ahE6dtsXb? zce3}uk*@tJ2LEg6hTyKXWSf%z;Di3HhpgWms=3C1O7qWYkMIjJ4fZ0b~zh+#4%43vt&lpX`W3Kk3e&zn}iyz9E_8wbER zo&EOb{jYD?f`?6>x$cQ4-^XcRVT&+*={uQ{=fxFc8O& zNYRv~5ujP*(7L=}S>z#m%i}TlQ&RE_xz3z$PPnd7FXaSs0_o;-Z*60`$1G7)P^~XY z6<9~*HG30}8pVnL$Zq&Y8!N4Ac;(Ye#@^C@$AICtpSbt%g=6P7vqAdubn~P4ujRH= z2&k-#7|#0%Cuzg6o}QO@3mu6~Y#=bI2P;S5WVb&QKNc$c+EBF7foly%Vt4S4EsrQTU2{Hr;8^pqh11z# zeBy~`HgellB(8!m8wLRE8qW-4)%AToMi|+yd@BPY7Yah8KoD8&>DF|~2XP=FUxqAu z($irtP3QT5Cjdr7kLGTQaUNs0rNX@@aS2>)!WLcJfT6HY7616{cNfV1R%Yy5@SNJ7 zv6=t=e!lBf)@+2CNAz=sC9C*?EXq26h~ zp2jkxBtFUIq1u2)_BdTI;Y~buMYAp8pNUj6F}{_FqQd!^iL{T+G!E0=5gz{`2c^IWn0XRV=zuU#%kS zcMl)h*Ff#M3c|VgBkUn5C=v;#Bqv+k9G3?598;|h69sZefo}v_oCt{fqAJw{&}lWV zOUd+v`<06CkRHQ#h)oW@jQbP$BNz$Rk%fQ6{lCDjFDKvf_JlW2ocwt0HnNN_n@85d zb@2f_{tO=S>f}q6dF0}^-;w7Zml0-~!v)wYe3VOHQj*EZaRH|+`^`23xV4av7IYB| zHo>h{L;d$$Dz{b*U>e>47K7Ysura^EiUIV-?f!XSMBi6ypM4U$7p^3qaBFUizKrv* z9Vg=s4c~^s&%Z+U+&UT|%&O2(eF_ZCPz@L9VV2tIEcMzUJi$R{rfhQX@w2iO_Hj#H z^j7CzETpk-!?1q1=-gM|omavdwfP5Y{P0_RcNX4Aqn3b^YN1Kt1j8d84wqhUGI>E8 zaJgMz3^!QaR;yIoX!ZODPTW^%7izfUz+nyj=u$p=o?|hOL;zlt75v@-jyuDun8Iis zi|GoEWRILheNY)GvYhA1$jHrd+HB7L;A>6;Xf6o1a56`ffUY``60JL`(ZR`Zl!TVp z))Ke+f;ruT>6sKevn)G%@dKCsupRTaEqUjo#fy)BbH~zezuUCsZmhp~FL@hM*miyR z+8Cmxl9hc=$TL(Bn_f>Y-SlwC(pHeqULzBT~lE@$Xw$Z*JY&|XgKW> zU%UCyl^9L`eArenJN!z%zWb5m7=JhYs~ZNt@X{BHALh1y+^f&r_BlWIe{SRC`;rYs z$Cj^Ymvg*kLRAIm+csj8=*-N<&nY)Gp;EceB_mdXwB4NJ zYH&hTnxSGZ#m-FzCsd^wD!hkU?DD^AcKHS|q}b&h@pk#n(nj`|tOdgYgcjjM%lxqz z4Suv3RBc|a1j{^;7N}I9jR3u1Z8R&wRdmLPX)2vSjLwyh88N!l3H)r9E^2hf3WroWU0Tr@rO_EDrl@qDkL-<0Pf4XS4& zsx1Wxs?_Hr8JStFeFR=E1wNmnF!d@7MM)|LQTJML6QN>K^t(x92PfcpoD?N1vvk3A zT(#+wP1}y@wg`pSU;mMfEMNst@CdSzqU>`c@yR7sv!M07Oji(JsM(&O9Y^gxC`VnLr<7QHcz+a&#jjD{KaG%9bLCgsUR{$1 z73-lb(Mz?F{-xUDl(K5m*)ug7BV#mh&6OY8q+wBMNKkd=Y6`!w^_ruu={HoXpCjI$ zDhh19)+lYXnUi6Ut?46-lNLH-guN;qpSJElWxQx~pv}jL`-jTUle#*M&RC6~tOWb+ z7U`l!XKbsPtVu>UN~1GQqm!3FXOwKy=#10obkgX&2%~3HE909tLE&3`mhTn~pY>&az zqd@%<>bbC8n_+9%3>&1*u#2e0R{kq(ubvAllyku!KRebiUIr!3uvEs2M&p=DgPseE z)s>u_Fh2}zTnDN1)zAlo!LW8`9^#OB8! zbsuSFD>7nY`$#BG;?fC9k&9KW^CePJDvwc0+(1eJ&7RLFWL}Fx6&j;Z2w#gr)pLwO zyF?1VQVQzK*`!d&NTjflQc!2k*9fs^B+?ieqk(JC;WlYlVqD-FS0V+FEv*knDX8;( zM{K^+%4_uR)cH=>``h6CCbk1tsO^>bfVCib2+M%?hpO+VDed9;O>BKssJ(1z<-b`A zGhhQ-A4An%UQ}1T%A-<2jmdhDI$B<3jb+EtkY+Ji4^~>BE$S|QEgDtGb!{5Tcs<(| zgYA=DyN=^c*qINT3;bw(C3%N-fRGTH-=YfTtlx%7p?QVlE-=A-Z7^}V<%Dc*XW zCStsal)DN|ZIHt3Vo0w7 zrdMM@+r`Znou~_yUoW?lr_)sn<3zkCc%5F(KjnTmW*+Bmd~=_jI{x}dD-t@vW{=CF z3GyH&wcT9inyZ^o{gTEIM`b--R9A{pF>JEV5(65Ws+#HUy3BZ%xxBgcY?W1JKJ(s$ic`pDj%G$|4Rq1b_LM``E!dFrRmBXW ziXvlY#ylQQk?+x$`T4)adpSsb>n9b!#oH6OufO_va^m3~kNrzddO+PwZ069bOVYu? zknpWn2>uR-SDHi>x==^Kd%9#ePh+-gzQH`3JW)fQWr2J|QMhoe9yK0rD-1CeQ<0tG z(2eW~?0XfgpZuHk%WnhKh~6^TBVSdCyPi@rEt5S?uqT*?Dl)ZW((DsI$KWqIKA1F6 zfPZ;LXJov}dGp~8c_h1+vvppk&OAVgEh*Tg>9|lo%lXSXL4Ag1op zM>}Rg@4=t6rr&{1iFdvA^If0+g@4GSKTvf#?82KsnrzvVQoK*7AcJJek%0M#B@C6< zd9hev>UmT8OHw&{-=wM=MS&&-PyfX3ettj5nZ?rR8)aQTQ2SlS(hRihkd^|9Po*@j zbq52;nwzd3FQR2mD}Nl$yGW*(PDUF4*XM9NHaV%c41Sl2?oi!6Nf_R1{JtH_r)Gzs z5-fcBKpGtsn(ZSF^;o_9TWRmCJI~Jef~VfpHB$@B7z&l$bMv{_58{Y9_qalrdT+z7 z!+vKjZ{9_8`RqE=oPQVi+}1#V<<89)@p2kK9rH>b;Cg#L8aQD5prs($33YBwyIti3 z(^V!Pz7e=hcmE3Wk^NB=@ODA{y$M(U0Ig+>FobRaNQi%T0mBmbd4JdWcVqKUsLy4{ zF^Dm5xJUr#zq$~*3JY)VwUVugVQyeW zB7DAa{&cpA1MH)tDuC>jpOiy}3dnS@R3%Wvm3^zwcGVti=X=!L0 zG;u5oVHMk+GEr5eL&_>`nbcK)ZZ>BV2|lzwU)@vrCVrp$2TYuAa$UC~0ZNcBK5O1Q zd9s`S&fAFopTilfY*z5WkqECAA?K0%M5M&1j4;s2d?(fN6$5E!~KT z#sRYH?3yP-`s3pyF5GsCXe=r-tZDXLM0yw%%iv_vDp% zAz?mumz&oUfbU*H?;fr{w~DjAOh(PUAj?Js_Z-jxdZAStv<8E>;pseLyFWpJ<3xH>bi}R z>k%Et`G-}`=LDZGt{!ZYiF91ABa8i41`ksn%-stj8?A{R160_kgOB#Z-DVA8JU(2I`9VL^=s4BgpnT750XPorq`{LXq7;fat zTKgjoBeL=vs+EE{3LM`q6O2mC^RBK* zmWgnP5Q&sLEELA^)6e-8b)Xc^P^`7og#b%5s{_|qG&t06ts!D;?Wm`Ciib6W*A%JL z0#9J`2_^qKNI|PLb4SwI!BH%I4sJFo%XiXmnAf$9bWfj55^514ZmDbFn|HMVC1je2 z;0jI5im}}nTgm~j$L)MLUV+V~ zt~$Z4@1Q^EecB0toXfvBsU7Ej>c~Yvoms38&dhVnG4`)Cjj4e4H1f&3N4r19xPlQE zVeL+R-ZeV3L3QeE$Ob&4^uX@7dr$b?9S6FF?ayqkA=%c3LAbloII}i;{K=1!F>ur5 z$r>60r&~Czl+CuPn4dq{{&EmtD~r_?Sta0$MS^MVdvj4!8D)*noM;JQ0yCXELNd2c5ns{sT3Nv2U7NncH^hn6PxWa z8!L+a7~yb;Df{XBZZcD*`0DdjlZB)U}X2D*bFKRJ|b2+c=e85Ru?`5ZqVLmqq_9(P zGdwQTBHnsssD431BG%gdM{hCrzyNk+0D$ie;K7BG>D6ZNY9)Bp@$cGvN|-eKxqEr& zrup#e1Nhook$f~Ql|MB}b4ho7rT;PKHsILwSdG|<*jCWQ>NIybPEaWJHwi+Pi51Yw zXrFu{r8?LSa$M4$^Q85gF0QxGyX%hmTK|S~gK6Wuli1hLh-cGjlO@3K_2aAh>!n!4 z_otwYAXjnEcw|j9bX0%hEIgNR-LMyA1N1bM0HPr3Dk>w{1!))+sz((!=Np|$aeLDf zC;b?V$jRtjVMoe6pB!54bmFO z+%mXqxpcgE{leUX)gM75g;IcUNmiFUmLQ~)SSjC3<&$tc46`GBMY&sCk1Nfn4hMmn zPau->Q+^hYDcw^eE)$-^Y9m_DI#cde3{ZTP{ZNcQV|VCx!~ZDq%X9{{7uO8J6_YVB zhPg+)$NsKicj?g{p&9hV|HLI2nLW;G1#+fD-&U{Y*2=c)ao>h_B6k^WwetNU>lMWI zLu)tliuB*YANjPM&)8-=ht@}@ekhK4w z1$^YvJ+$2aGP1ji~PEfXG*wGI4A119>qlI-FF@F67-}ZDK~_*T#&-O#QgEXnWdR026c!0688?1}5n(Bx-8RQ%ksnripMIGA=HV z4&4$Y%0M&XH9}}BYa96~^iz0P>3GR$!D1dL@Ax46V3V+k09NL+bM$4DPUvzJ|Cb1J z+jP7={yiQ|FkZy28QAREQf-;>PDlAeY+Ta5@1ExF4}C(d!CNQlwd0PL4pc{EN2GK3 zbGA7+BIKZWL{3R@Yg}$(T^Q2O)#5eVVHXtX2&>zd7g+c2x&^zF17cp4#PHUDLck7S zGmtaXG+(w=;$FubD2%7qhe1Oy3~wWB!(~&>O`A<=z$Q$0OrRayH zQji0!{u#Im3f5 zGjO~KNilC_JY`?|oa#a^nJ$suObu})`yVOVIoi2i*?shbG`S^m<-SK=Z8o~uhHv#r zgb9q3LLyY(*}3x_i}OJ@u)%z`(>n^ojop%#4i9eT9|ME_GYkP=u$Jd z5OcNpZ4%~S)IZWH;PEQ!H_gC};tXp|x)K&!c-)Gc6P;!n1Nzx}&*U!y!I99f&mf^) z974HDYAzyMmx-Irya7IstJVosGFObR)cV_VyDm%LrTgs;d$StmfVRS3(bduC(8bv8 zoK!Ew%QT0Oo{?IRYLEw|1*F!7qO1=YBK*G$L{%2#F}$;lSSY9=VA+d|*QUMu;`%e_ zo=$C8s6MNbdUY7eB}<>8@T&R`!z%=rsg$W0Qn3dqC91Y$DiJ&VSUN{~t3p4m<1TKX zsWm_D<|(ZHVNuHGQ>IA1;~_|3!JI={NqTn3jWc8%wqZ&NHC@Rvm*2u66SP;OWOu#n z(%cL-ds<2GfEZZAEYxM5V)@LAG?8a_DBD>zyRMAyHoKL!mv&|uDmo_PU&VT-{qcWc z4@ivjJ$Agpc+z^ddhwJ)&&Xp>4sV;nZIcWjKv6Sch3!#--Tgk(^N$Y)6Ap+3-~qG& zaBm(OCV&l~|92Y-1potp@>+2l50a5|#tppOTp0<*l>6}twX%+v7o;m+R15pPX}x*$ z#LzPhHxSS%S2GoILm(`vAAq0Od+98)DZbfIt?Wn+Ob|rZNbtPrls!}Ml{(|U1bQzf zoDccLK1+#QEFHEd>W$31IC7%&NI+7N_mGRVG|Z~zfunoTIxdmrsHR-(taX?t}p8#;N z=DTcSqE9w-2~5R%nw$|57BGXT`+a$Hr8%ah8EkQ#U*UGrCdLJwXQ(kRRVJfIfjI+FYDdp*b32}{_|Od%5{635 zVLR4}lvMIQlf!PJ11>6+S+CA^ybUe0*?zUoZn6s|JeEYz`DpALN?NskyYulxCqi6~ zkk94OcsoXJm+#Z%@nm=Hm$LNM&qc|N(Un;p!3Bx+k?`UdW14@x?28|4c=J{DTd1s+aXJSw@kf-bxc zzc-vTtp!kn$34ag4Wz^ZCN&WDgOQ{*OLF4n;cBUEtZ%MsYVdS&d&GwQfbhl`E?^U! z^4EB}J8bVFrqL7JX&2g;4w8_LOkvO{(GOM7fXtU^q|IiPFXpKvS+eLh*(@g1`0n#K z3b-FG6l&%A!@#2v@|w=2JLAwPW$>CU=ueF6)mk&2Kh$R7ywj}GX)&2iJ=Nc1T>Lf+ zKD$b<5L){0?|8+5#en}CvG~5S`KBIM{TAsMXqc&(=vXNkX<4Y>(bwda6;&2MN~-hA zi>nGNN*#@@&FxKXEgej3EWl=V_ykLWd;|Yygqq9-9J7PmLO0gyiqhxdj@?X zZUV1*G)M}0-Rvw!X*q8A-QVo4#u){?z`=%omX_-8qopY>vM}G>0pg}4YpBo9@o+aa zy1U)pprOV@E4?2dV_~kU1lymUkdkC)8R(y@tRn^^tbGJ90F2q5|6haueU2{=xZ|-+ z=&Gyg05vq!4A6MyRrN^S=hcj0(0FFFag=yw^-)-wBKzcKbTj2*8W|RN6RBwxewyMA zG~EM>S8T|?C6r2<9~0~f99BkEw;htT{K_W#bX43Taw*g`IO>IEaY;)yaD0#V`=_%| z1ghWd>?QyPjdKZ85{+vOkvfKRg}^L^YXcTXiE|lOO^ItAm1~@HmBe+NYZDrUrG5!p zk)>e``CV203Xxq^!v;JtxPBQ=58SYhE_hnMO6GIgunG0n9cT$lmeh3(i9Xw9h0rS7 zbpsCHz-1YzW#GDw23mJnCG}i)-GspqzFNZhAbh=s!q$1ULhRUiy@5dPceRXf6UZ;L{D22W|n8K^pME#Iu4LLeqCHXau6 zO?Zv_7h-CciQ++5vV-afra&yoq; zoAkV-F3bF*p_*fkfxi#caDwm+f2J&McpYc|7P&u2n(Vqi$~^bKKTO+vxj)W*mH3Q| z!zxk4Fm2iwiNhvdKr^gbq>#iaS;D;RvNMuYBbHCquaL~7RxMG?G;ccxQL7Ozq#M_5 zw4JS%EM;B)baOQ8_&txZS0%i%{6Tdb`ZWLfP_hfH2Brvs;4`15sZe5jIEl@j>gPXYFhviQaH)#V^$@7mgsGLWwhJh_5ud<;4bXY}@7|}w=PQ4o0N;Fmdfi%(vSp>^>xoZs!XEgu}h&W;R&pwC(KdSgTEBbV}a>8C!wOG%YV6f zWUY-2ufG)~Sj}T`_TMLBYtCBmp@zLDI)OCoC%3LFO?R!{JSHUJB_pGo4kum!u&b(w%&=xSuYM)Qm1IJq6 zDw{j-mW&O=o(@QWgGd%3+@@a9kjBuhR%0r(M*jA}I9%IB>cLx_GAzMT^i{n<5zmvS z7*fN$1q77SsByKIT3#4dY29X@FqE*K6`D>0ECyvebr%M%#@&Hf%dL8N5d`V&?{ z7VBY!<0c>9lpr2GilTxn&M!YThMAb|_AM(fM2L3BqA{|~=!=9$Y$C-cGt@82B#i~{ z{U8)eVUf|#i{7udFy-y{`12|FT8r0Xu&2GYaNAHjQMBCX?!V+f69na%pn|RYVT+6g4k-ly1Lbu_6*XsFF-*?bN4da@C%5!HRcjgZzA^0N8wnCG0XpM#g7Qy#Cs1EZ55*hv zQY4*;&V%NcI>4=kYWR&FEr z3(vro=&CT}Kkq|Vu(T{IZe<6mv+)D;3^vmxlG1710-c=$o5_vVv-t&zN|Hq7+k0hB zqkqzwkxAf8jZuH6ZQ+UfL1Rk`)JnFo@SNO!93Lp*Jlnn1IdlBjGQeB*k16>_`UQ%I4r6u5)>z` z>atKrwLUh-JrfSEAKUYNW&UYXhgy2+rVdlgv1v!aGXc2BI;fe-$Qx=d&fv#b)d;%pBMF!2?&!Y5>}b($m{sdzV!V-kS0a+L_MViy+Qm)lBfR zVesv99koeuQ0|oKi>ECBDY{klv!hBvHKGQX$@j37HL3mPdyX`umv&R))?JPm|XJ zU76K^%uwXk+8=J!gp zL<)krTQJK4g9v2rZuV@e2X+zA)ho2z`dW@okKa{3Vf)s7+!i6t$>xjFREuF1(! ccCWzC1`Yz#0Gfm0iAoT0{{R3 literal 0 HcmV?d00001 diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff new file mode 100644 index 0000000000000000000000000000000000000000..724c2e64bd050207f8bc134816471c50b6bb0f1b GIT binary patch literal 57664 zcmYg%V{|6X^Y$Ivwry);+qSKZZQHgswrxAvY;4<3{&{}ym$%Mb)2FUe)!kFmJ*TIq z#!X&K3;+c9kzD}*(oX^8*Y%_SkNJ=N|3zFxRP4vV|A(Xd0X+;WtfRPsyz&oM_oE~J z4_E+Lab;DZAFl65Z~iIy_S{am<&_zj0RUk4A5QNF28HW|uEw^84nN$#A6NhYAmQaJ zw*$tm&V&E}n81&X`43vq?Xk4Y9L#NhxIX{@XfXf)vD4{P>|$={^y3RH^JDY>+C>47 znA>=m{ct(}K&L1Gh`Yf&?cmPB)X)R~82<4Cu>S`TP~2mSAM%IW_|XY}KnnK--C<$t z{L{DLkDvM>004M+uKg)F8++p)yJ=7W0DSR>M--NezyFO5-@hKcX9>*mM#DQn4ehS30&Xg3^@EY{qfyl{IP-m0n=UqxU&12 zv4M$!!H%InfE-+nuwbHZtazpvp0qimE1;Oc2^oaM3Ev3^gakl%2;BM8!2j+HGyoRx zQ^SAazNQ)lVh5&xDl)qfw$_4`YG|9KXlpF(m(+|D4FOaxlPF-m?uJ2{6f->~tFGL` z$)PMu)77;DDR39|#7RtiIO&nds3gh5!?$7fLZEk*%k&zdj`4gv^*!9_rt=x)@8HiU z0D#<;EJ<`tMIuZ@nztnu`P#l<3W)D~8IP9f>U8CxRv;Rv6e&4VCHOjC{_GEMzN0Ej zHOm@inyH%hzDe#J!fXYP)~p;&7Ye^s7sI5{{$Ib14a}O2)eHVFv+cwYDABTo1A<@W_C*DVW$8V?(mWS#U*7H1QVAB5wMdXgKyO->!iKjV*B6mq40CYX>DI~ zZM3wqHadm?fHr^;ayp#B0(c5|K43hE_)-09>JZ=!f5}9!)uFo%CUAmb?EA3NVoQ(y zZPc|v=ShzwE7$t|flT`G~GpcZ&F0oi;jE`3jze7}A1X?>#dH?0jNnnYm{X*oc z;Xg**5jAeq`i}xXa`Z0{b9OZ8A&ww}qcOSPNjxoKW z5DKIAKbuCNzAB@FDQXrF^nMje!-lnt_tM&x`STq%=0jO3fI#oxT4#^Xf%VcnFPewO$ybJNzw+HfF+ds)XxHQ;Fn7Gt z>$0eeV2-Nzl3}y_rRw?;ce56pf{Usx;Ia38!Uu`+YRkpGJ#BN@4wRz`X9;ePx%Za; zF7M9%$(~am$iJap$N$E(D#YHHpvO#}2R@C%f z9dXqOPUsCiO2LZ3D#9GbYRAOF>|xR|cQ$M?U4n>&W?^PrW9$H0Wrj5m zusQ<@cOA})HPPubjjd; z1Cqa)DeSrpjDXqC&jwEFAKCXLqW| z{loAdR7Hp5iYbcJy2-nCTeNFE77zqQfNzTYL|U}46OqCd`Xqvvz-3b5wdgPEoRy@_ z2NSdQs5_;oiv6#3sXDwpWePq@Xh49@wR(h^JuOP2BW;VqY9cGp@L%kkjTxMo^pt2> zO`UoaYU_&?MC!wlaZ?eHeP6g&0v-Q#W@FN-j}R`EV@&sdG99+3+Cn_8QC=k0lxir4 z1^1J~p2iy`NSR9$m-9xZb&_X6#ibk_=sZLQ_CWgV^D)Ho2Td?>661pjvGFv^Jj0;{-ih64tdO7yE-AbYu3ad6f8ZE+@**BUZJA3E&*YQGnchnHq zCC}Ncn9(#lS?U%9VRZV6W+$9qr=d?Dz7KZ&<9B@XRor$UuQ3A+V?~dJc=*mWoTYx# z#YE`zr(YpfEEu@uK#Q!g#u>K_INC{cxeA8Oh?e0@gfS2%5mTJ!R1cLAH?P?sZ!&wV znb7c@K3+}|zMMBz;0$c|37l3mtKkYkD&77jn&YWRzAe~8!DXR;01_r?Z8f|*#{qIy zaelmSl{OP{%I03;5F_enl3w~}&ocf902pGEI~9MR7PHncXk=B9g=8IhKARMEz9n77t=|~_LMo5#L6DSn2I1^zfJohMpfG$ff|Jq7v zGc&$*q;z?>>Lj_(p$)-a9J9QN6&9c7pn|Iawvs_W7zJpzh!98pmLFEti81)ZAkyer z0&}gB3`W_E)Q$s(j8`)vAR1&1Lv!m;Yd-$vZw^#U~}S+2cx|q#}lm+P7zx1V9BErWXmn5?YKJ?kuc5?_1rZfuyZC%HDCKGiw9} zX4?xWvP1R?zQmvska|PPCtn|*Z1rz$L&#y=G&Gbz?VZ+E|I;ax1$zNQ&FWqL1|=b3 z?)_PtfB+x?&;S?!#P>Gn^>XTzw z&aTw$s-!iKtFE-&l}_~qw?0pn4>ahA{zo+8CzmT_WPACz2&NOER)nf`toEkJuj-C} zr-2m$5h%m^4-`h}q4oBnO;g8@e-g62e@(cF-ePSr9%pKZuwkg*;Y^0=;g1s1B%IepL zItFh}$if&kdo05~=BrdiYN4n7l_D^B!#HAS*S$9GMoDVNDgwM{V;*R>HdYjlDIWnEDB7sMVZO zr);)Z?uE>^sRhnubI!eJo7A_Bcqq-Q(d#bWdaE?ulD^KMStUJX=%k^e&7bdv$zQ|6 zGx<#Xy>8*0Io)?Mu{L9_Cg$eKom3{&Oo~N`sj{*%(>LZa(eEEjaRf^dw?>q}y_}x< zH9{^*MxzNv6e5ejcW1zU%gf5-@_!Fy*S4u#k{*J)K7h;OL?@{y-j9wk9>@=UXs9QP z^z!nK#DA;Qjvn8?7bjV4h2zZP@pzMP*JLoZSRHniAjG{462#(rd+mCypF^Z=xKDd^ z^0^;dtScy^#Nsoy{E=^S`z}lbbDMrWr0HR+oI-hxpaOf{-Mf>x%#~gDBk)$f1cN@% z{gBG4#078M;m3m}gl?|ico4|1a}$%3Gc@#Iq?Z0jczdd*w@Uc%GrsHv1fu>73!qk?y&L(zxifXDb3 z9#7l5kw77*+h6laLp?}r$5fCv0gU!ePJ3DV#{kBRb$0t-!{O9#XISi~YyR%1;|8<^WtrBVxMK2$B?Le*wB} z*%q47erWcw7_KRD6jNB%X~FWC3@1TOK{q{VQt2>h=o9C*k6rJ;X1A*0-PY=_SLPnC zX)*6?*iqb%Plmjs*ALd-zNozQ!0S4FDDITNnIlv@L6LZ5=L_7{}SR~&(6J84VyVh_9FY6V9mj^wV?xN47&KEb*NKKKOw>`_1(Fj?$ug+xO&0aJ1j=atY5)zTCr%deNl z9OjwBGaA?D)w&zf$#sd{sYY9msoaq(IDCat_W{6IBnLaeE{&98t?LNB=?Gc%K452e zX^Dau%#^_1#nx~+Itn^CUm;S`{MC)uFR)gcWpi#9ZRx73>84q<$=DW$8BV1Vv?+^+ zr%+KzgB8U#2I9xl6AVHOi)nb`?rlk{aQO((pD38sRxeD{sG~h-D_~S0L1VT;+Z2nQ zLOJ1}`3MQ!B?Vcz3A5qkKD{lE3>!Vd=C(Y1(Xq4?1f7nXgT(&Atg-8fa*Xk!Ez951%BFW^KD%Ixy8fGX%(2zIyUU+dyfhecr44Ei zrhq{MdC*2@mttlD_=DbD7Y+{6FJSx&llAB@1bfdTbDl^hkvWvs@j<#(7x`_zNQxNK z$y0+<99VBb;58wfC9^sze;#^QF*?Q03)Ftc>t&P!PmA%4UW=!-9n5Q3yn6d% zD8?M0Dyrxsi8M&!)^(wxmOsBFY324Uzs;{^*%hkHRG4q@H?! z9Xr(l4+-7e?K5zD;gvZf(zf@+W+j}f%$=t6J}Etx%UN;sjpF^+%!tlw^U^J9!TxPv zqYNhYBvfgmbJ1+Sw~V_#&PI$QVES4<3-P%!vze&IN4-0qT4p%sK#ZgF<~>;}gdN6Y zxh|!TPhsEhunOrAzGQ0uXnhv6eW-vdUZ#sT{^f2_NoL}XJ zQpKUnUwc`u&?3(v9s?L3@gQvVI@5a%Cg#E&Fggi3(xscZtC+3p-BUOg;a)*g|1izh z{2ak_!??K*#3n!FHZ6-HaDfwS6iD|Y0?l6*Id{Fi4bKBX?6K&kA)bB5df!6b?t0^^ z&g!kh+@AK+G3d2E$6a4)zkQ5#9M^7torQn|iP151dkTy%A6$b-VTYl@ z(OoU-i^;XeH_NUvG$qsUvI;w9IsNW~^LRBAr#!HT4ij2BhG8G_!DeHPwE57l&Yha( z0+Z|Bi1-(-cPYq+TFH0Lnz#oMF<*=K)x?d)Xg{)8RvEmp-cQx=61uX%Cu+4ScR-Q!6 z)B*VZO}F}~SK&6-K{l3}EEe0p8}!~q_vFnja#p!)FcQE?V4|`XloUq-W-N?Bqt>qM zqto!Eg5#aMj?_>K56Ik9eX+XyosVx^EN0Cm@mU*e?sR>ZyFYVmy%f^^=U=8ez865RC68?6TT?2g8ZRI zmO=}Ky2^fYJQcnN_0p>%a^k)&G3T!Ow-s=zF|>@=rF92^!Cyonx&cOn5IWS*xVR7q zP?L3Phpu|f#3M9{GhrVkvFoJ5Z*9cS{X=nmwIgMrADV>E$-NVdg4SN)+0C1I8~CQV zFBXmOzP*0_74)?4y;Z%EdSAW3>Qpu{)8+V9q2cWPYFe^Vv>)s9-FmWGFI*W_Wn|=N}1eJ>T~bfL}>L!CU6W{SZo|jx7^VQ8Dn# ztim;ONHJS=mFE@M+Xb02q;`v zl@=X3U$K;_Y*6lO54eK}*j}0UzZh)g>*dY9qfzQSju%8d^2|}rWUj_I=n8d25+4EUBFRs`ocOl^SnFntJ z7?)n??s|=;56rj!{Y+4s-EQ!B?Ue5GT{P81jk&&V7mLNjrtf=ylJnt>VHD5$YeJa# zUm(IM^w+nhSlL%5gk}#|c5KLweEKURvl4o{9@70+l$gM=&e%G}Zsi!~_8?4-gT+EP zZHJpCT{;He5x+qKA83fy?|b>E5nIzrKAx#8n!8u0eFrQ(Kj19Ao=}2`YZ#H06`=wK z5KhycW+oCwhjz5{e{Dh;)$}tu{5q9kISmTeG%x}M5T-=-`_DJPzv7e~sq!9A{=UjG z*ian5ZhnZFV)W+Ob3LeyCOiWaluSevScSheB-k?I1AsDyE3ijLOk0R1{&5yqR74e3 zmZ^5o6{gJ~pFOgW;2|uk33_4F4!a84UYB_@d^1Z%{T7^rG_7Yv1C#@mk8dO;liu&M z9taY9Gv!R&lY+f^e{pSR)nzfO{-c8fA04m+s?2I@2Fy!%f)_*O0h=*%o$Zm2cnif` z#VoN_v$|1dWrKn=gLUbR?V^D#el&@Q;CTv^ck@!AjN_Y7b~fB6fq4e z$=@CpRPEe*aOK6}+%Vb^!sYM`x$mMRg85^V-TX#IQ?WseT%{edXyTDURVna)2p*Hn zM+Zt?RNaj<=gxPZX}ZXqEalh%-|C<2##FuJuvHPuwa3_TMNRyuMV4=gq#m(L0Zykw zrwW+1b3fhN7Ew1RFOqEWWv4unE-9zJLZ+aKqeryr;6zKo6f>nF7>HwtXEv1;;_8S= zOx)dI0M+I)!0MUjVmPg&3j`6J5CclDAOhoNhp$PtiC9qfyF=HZaB(z*^JBSP(USOR zhB!0AM@WYyXKAL2v}&wStDO~%X3+H}IhtRM8*fbbV-~X}kfP)ql^~?;(^nxKh?J&| zjOs1j&=4Y+Im6HB`Ko3#s04zJ!m}+xC$=@i{F)(~Lj$H>mT~D#r>W;USq?*y^rmX- zSra{6Wzke#VFm(+E7xcuvqHa&#iBfImjCB*$qstjc~i_=GW9ORLsk3ub%r6@t{qe+!_!RDJPY#*i?1Wc2DngsR`U=ty+11*nNtufZ~LS?3I_`f2Q@mv+ieBFwa~$81^4^UbScWN$~CAyfS}a&7f@49rL58S}k_^@jdAgKi7AA zhMflX)U!4+#^~PcJTW#xY8YBiv$5AGTE-HDYx@X68+NBmndutzJ>BhpT)%qBr^S;u3r4~VxmsEW}*#HJ^~>YWt5mB22G<^ zMXjgz(`hh4%3etSAqJD#42XmW8`MzHy9tV14P$8!a~dL0jr&~jCWjXuB2Yk%mu?7$ z5gWb(jjp85j24RnOF(QL6#$~$-vb0IsrzIrp(Oa;>C#@#(ALysimPWY7C{|ZHpx)O zp+a@zQNSJFNaF9?xBkWF)e--dqaT^AJ-gykLx@l7sW>yA31-9|HI>D1k&6Gc3Rv*L zrlD*K(PT_#W5+j{`pC)78gS~~FiOBThV`+_!=XQ)=`u2#&0M*QExYD(`0TPbV~atE z+h}myXPgrMb$eJyk~IPwcfIX|aHaS86X8;ly1B(e7<5^APe}T?t4A}(`XrhkRgk|PWH#I{XF;A943YJAF8dl+= zP?W{D%R6zQIemYDMz}}D@85)gC|@&$ZwIGXuzl+-gZutO^G5_`Oooocn!}Z5!Men6 z4Mc>bbhVO{pB)=(NT1FC>g?vRruO{ZKvU}6no&rVkkip<7MW(!lcmkG zjanr>v%v*ZHbo(tLr`-d&pIQD!A0*f3P|qmxUw>mE0R=J9kwxoG$@%WG)`j((FN`Z z$CXsNppN-e0!~mQeJ{5HRs23i|JR(Reuwg*%!es|=i3GYhx>e|^lOM_Jng{N7wqc% z!ELopul8!fSgn*28`+52A`_{D4ief*N>_y%K(rw{gQf+i% z=Ce4sCQ$_(xX%26+FF2iu`k{Vn=pMUGk?qkPjb#bwBFJA5jX4!mpORwFydA=o!7|_ zM02S0kf)>#er^aN~o3xF2)6-w;2N{M8N( zR%e@rJ`(^8D{(0jD0Ff#T9ofhuUUHL=O z*G|ym-|@58xb)yl231?*wGW8K`=OUr_aFg5A{Ctq4f8#53obBXC{z7_P1>+Qe{{4z zQ9O#H)&>6iSR0v>%!UzATo5Q487FhiOW%4--Esr+tW|E|7okv)^aGxQ)#2v$D!oy6W#aW;0bs-@r*5 zzaG@g5ewQ&BP=V;Kmt?)&q*r8Z;;R}aG^7C;jP)57qY|Qs`S#ZRM?zEbRGljMR0M` z@*@q}I?8a2Gj!&Wy(8`Pk~}s@N{i7{{H1=Y9OZ-P+3~ludcLJw7$EXTR@$^w$oh=L z;cs$QrP3YS-zyi2^QUi>ZR@m)seT$Ln}sXnX;{JfX%;1)JIRG8p>2l_>$r8oN>e;{!n^mslM;Avmo z?;K(du~u0ej0$@^*X-#x-w9U>I8SH?r~VR>uGer42g9?gT<*smXwt(;*TuHLBH7CX z>7ruyte{HKO3wCHx^fs74AW!cF7dI{3}LiI4!ms??ajDdX|37f?AsY>?&|L6Yjl{v zT>HmtCRAijr`O31_o3`p}SehttEjs13o$2)bI1Eb2@WD)zYKeusayBLnS1l z*}<33BQfs)f(fD;^5hzKL~W|`XoW@liI>}^0q9(EBdB{Wj41lfp5da)Q%(@-yNWce zCX3VK!&#z=ZvN0pf{~dNy#)xgE7$_M&79l` zK}`VauZ=-HL~h#P-a_F?o<|e@(1wukj;Dp9#^^w`cz5Enq++xs|{ePy> z(dg&wWwJw}RVEu(S*MfII2>et&F;2)J8vY-3lFN2A?Kn1<vrNbiv;=m zP;{OCMJLO`(*wDgH#uo8-?d-9x`_WLx6NI#Hvk5`zJ*uybF}NFg=jhtYp<5z^8w=- z4}T|XtIS)cH_#SF-*>cBPvIRFbG}x63$WJESbvKpfS5nF_Gm-=W_P1CooRN1{lQn+ ze_BkkCl`j)x08Z-i-#)%=vLMjn?J_!yb7Nn6+c&(%^__a;MDoV&wgBcKj`>=j#6)z zee`a6jVaAEe&-Qpey2w(i`Rljt!nto3dAUqrQ5hP#763x48Lgno8Ef5X55lAjFS|M z&&^X?``k-h6Q`7ojh*cZK!|aDjGsuAYXipr5c$e$)+lQrLU{79Jd|V66htk=XSj|U zGW1V|p-D7=!Kpy1GeUR8TvD%UTS&*nMm-S!`Jb%DnP(zhm%{5A#M%d`t#00gLxFtQ zACX1d$Z->P9^GUnSyf$hMoM^yCgMj({ym2C34+NU2uZygx@Wq^3e*$&TIu z3`5o4H|-^R9V6nYQyy7koKHG}>l-NujQyOOkZc@E}9?&qFM z&o>n7pWL2z9gttFd=73Ol1;3h6->i@9MRO+SRziLCck;{EQ*v#Npn3d*dfSR6+50lNDSB2I2y%tFTibN;i9e zi^IWNV)`ZL^KA=FSh_P&k_EPfnlp_x16GAzbg2$`&h2!jMIM8_xu7Fn0s0SbK#+0( zP~>*w$M5wFg0SJU()}#)ag6UO;;zkoUZIqIa+tU+@AU#7Bj)u^>_bl5sdz%kJg57{s%>ZBU1Zu1mvi0NvduaRCZIuS z^}HA;M?Yk=OS_C#=MQstms6lqsR|RS5jviwi1W<-$MT?r9@LX(*$D^SbLq;^UnlPp z97*=C)Nk~7(UVgrzuwCf%cM05TD;q(!B$_V4~qBN&Kf)O!zCI5?Y4YmZP)I^+$hx$ zpLg>V*cjd(lS!5mGQ5O^#Hqrw1uAK>-owD{b}3%>!(61w`Q6*}7<#K>zTVYK2ZXqF zBegJHuoR4dGlIufcyY%Fow2jZWmiZ!Z{_^g9&4 znYTaadcIHglqp`A`1ZiZN2QSL3F_Ey1oG0iZ3yMcMhOeEDd~%J>tjynN5CL>|5nk{ z^(ij~*Ck!iKBn>cELQdosxGs!;5aUac2uq*#LIJYOn;shdEHdPJd?gFxC$vW3yMye zf&>L<&=&qC4R?h(8{E+Kq@cA-LsJ*;<&cP<_C$Xadq30sz|xC*?)}+4vZxVuC#W^I z%~?zl;GCUzBlM7o7l8z}APY1CA2NOgpFaD&0DJTLCna-X$gZE3sc*u>^L;vdhP7zZ zycwEaFS)mlyib#K{|_h6@Z+c$R8u~oP}2b^5j6$mn`#*L-!|IGqEznV`-~4pdi!=A zlz(n?d@RkaqN*&})otMf#8`2)JAMS?!c{SGuj}1(5;uqppBA?#D1$ez!ACLxXh^Op z1soXv3Ph2l45~;$7HjlM#WeqkfI!tPEGq0Nz0fBD+@P|jUdT`s3rctWvDV!yyxAT6 zFUXmFVST?CeaX;xqc3#$vyc3HHk-Y5Aco&ph6FHt-i$=(j9$Os^WvD2>(Ctt{q;X4 zhDizJbj$eiw$HGUT1#;RZj?$9+JebT*j#MMmowHn1YMR-Gn3>>)nSDaKnZ*YOEF<% zKid*Uu^GB_IeNS;wmZHI4R5mw*#_SdA2Gyy-ZI@wOB@bV{S}}E;-L6qm}mZsu!1t; z1TQ=o4&AgFQS4C+-f(*MvPAb;SUEl|Sc3BYoew9t*>y&$9YKgo!qV{WxX1Fj>Tg~A z&BPxLSqtCc&5GLa>Ybot?CGfonk^@+<(WI*mslFlY&-zi?7#SnQsXaO7UP~1Q6lPy zHsKWcr8qkba$KAKHNWNV^3_!`>HB;2rsLBu!}VQ$Z2pC_$Ih1RJKK|C_E>*b`=G(y zE_1M#wrxZD6{Fq-XJ(lLgs^5ucxf|?+?8fSu9@^lc_T_nE$%285aH630m1#^0a&TLix}yqjI@8JZNh~@8|RsE=o>n z^*Uptpjd?9>gxlXS=oM(w!zFqqv>O$#@w`k~SlD)ZLee9ItgfQ5~!-AcY zHcc8srN8^52M-}lwF52tBOHyju-h)_({wM~VEq^7oYIn||4=ASKbfd|6LDQ-HhQ7R z#tt#oBQ@wmp9O!7z4vZ=4R$xat4}Q5Q+hlM)H|wLuDAFU;9||jsl#JA;(d=UO&h<^ zXs9w9IQO2Obe|5&`}e7E&N8pEyz#xz7;(Wzt^avh3 zhqLq;|77|yS-)_X{uab+v^Nzj(NA4Y@m;mE@~svqC9Te9=D$ufx(_KNW1zLO$Xs&2 zpOlV@MCbK$>MD167&u19Ad0R$*`Ay0-PXAC_1+uMjf(Q5+iW*^sG{1(;A^cTwEuE~ z8Et*Q;VJd|uHOgr1NvHMI{P5*jXrJY<0h?LxcmPogruvW^@T}D2n@yQyR_@dI_67k zx+YEASw~vC2IT22BTF>#L8U<8DpgT?tfPo0jZ-)-@@~g?lIXocrHM}z-31vgJd{eb zWyr6d_^@JXCzjycUbR+P&+xNd8v;s&|^dFkxAQV-psm)1>qODE= zlD*x<+ai`{SJ_EpJa`QEn|KT$0p~+{1*1#EZMf71I#qK8l-&(P{Jh;$2O#>Uy#{%F zAx&KyswWf7CWo;R=C80VOpYK=8V9HC!jj0BSHhy~Qo-gGb<$nwL~;u~hq_<}_K6fd zmGP}-E-@}RUvCTNv+@KU@`u@y>b#!{RfgloVcYF)7oCHD;W8TyJzz;>ouaTypUV>d z(UCP?=`@zy77c7Q53_NRw8-H!ot)^M01L&%b?1h4xYw$Sl!)|?$ZpCHeO*{MU$=Bvd zG&h5q0+&wOt$SS?CYM`yn;RNkPdD11yx>sUFGfxoDq?D!o=Nj5`vI)BMK-zRxP zVZ~6e-|F8{T=mB-Dz_q=?j{SYk}DLe6DugNiIgf$fF2^3pb}dCxL=#lna>{Nz*Bi^S=I2!0UoP3U1VpV57-bdr2rVO}VzIc&keADA@fmrzT$~(PN_oQLklQwd?gOU=GeAeBF#_r=EibHYB~)r$ z-EGpE5)(;564bMgk}BLh)x+g&?D?gT?b$;zxUy(J9((m{a&_EEMnCqJxs<3pf%Iyp zH&w}(LE~P^~Nqxa7g`Ii>vrr3(kX1Mumq^DQvi0 zv*`o)2rZ?9iiZWcL0fir$S%hMAx_4_Xr&XS$1Yvc1|vGzO_4#A6 zmEL`Kq<_$SB@WY5Q&Us}PkYHL323Vz(Du`mPLOSQCMx4&}1QJnYN3(k7><8aB_{z8BOuw-s%UUwghsD^Vf@4@9c}2w>RSFg&*5lY%nuoz`)o;5WSd`Si< z@yl~&m>P&+Y9zSEM_#ZrsF-v>bhr^PA!;FIDecr(|8s`IHf||2!XRbFH2?CS`jw&1 z`d!TO;lE?-VV&}qNheZ!haA8R(>n?F$`r1~9zL#T?BqMQiAo0xa^iVC6A~obT|Jlc zE6*Qy2|isF^4nM}TAH6T!s5B!Pko)=9F7CAIm}7X=r3x?0~}HP1`E+WaJN)MaNY zM$q;+cz3lqKTuz0HRiV2E!v8cVNdK^j2EWRU`&s8yr@K4b}u&zA)x{jqG9(1n+2ZA z5sA|WHe?zio+mu&lPCbUFpG?ZpB#m&{hLNy6&v!@lfrl~2riLFH9}<$w>+kuo? z%V0E~8`6+Tr4Y)1;RuU=i_m~6M_vG-AU|ukAV=X8mArM2AGUKN5(%8?N~HD5Ay#`Z za%@`MYa$#rE>$v8bS2?gp(#7macQd#PX#J*O9{F+;Rg2SiXDR7Gcx<2s!+pUtCrO* zZ-zWC?zYsuzmA%JNs2hgfyq{EF&LHi$UY)FbNoma1>_Hy6`)HW@oPCZgpFq53BsgB#YPD^Bpj@u*V3@S(mUTw=&&ecy73P)T;zA-PI zY;IzCK7c^95$lE*1<#GCoN9P|jIF${CQ-Vs9g8*cg@Ha*bL*3@86><79%GE3O6^8~9#ue>m?1vzc)HQOA9!BKk2BFYN=AN-lxZ}7KZJWfHiwOv zrCPxapRGNgX*m3X*k}u|ceb@If9>6L4?wEp>#F=L$cVF+-dqct=;o|w=%LQE)=t9L@`BYtIRrap#bQ1VT0 zVM&hDW}xX7$smYlEL^ykimhT0$dz=RAIM}hB#kBaCcGn@ahr-HE8gjm5$yd#*SJz7 z(bxr6u%oGkT%)!zw#uh0TniLL3(Ezq5Njc=%pW+0Z6eF*z@S+?9ua4Z^D%mCl-@u! zMmh*R8(2$tUI19FtJSR%f9>hDO>ry z&MB9pD%VmWZ4k#ehuFIVkB!kd&8xBLnbWy~z=lDVVJQ$Z3)HA%*Q(!~SIa(pDeXT} z)(WbF%_Znc0|kZqWEXf)2@hcciv%hdDUdmtcRy2)S~tXd>S9d^IPja zjii`wb+h00IG02qT4Y(*s?mHVQi33tHm~uz2px`tTXL<@YU+5kD7b^>IYUMFB*{A%dNJw%ds}4 zrtrY2D_9;H-Qr4f4(B%CV#KiK&dck&djq+KBerl(60=g&6n z4_6YV3MCw>YiOh_H0ji_tJLmavr)+Z!%1Vq9#%$*hu^6A;1!Q~80+S#;Iv{oA+}Mh z-v;*^9$6{+_?x%^6Lsb7RB9)6e9qhl*qZ0bveyb^jX&XIjl@_c=JNA=w*4en8~sY( zYwP<%mdDh0ePCRjE}fWl*X7*z;QPq>;5_+f{db!i67^OlvoYU$alIA;k~5mq@Y>m^ zOjYVyuwNGxGkEwA6U?lAr1&-yYh>$3XWJfVSi9KLiwYl?Z%a_J5*58d^YZvt%ahlr z3;&Sq9zjYzdT5?w^q`jv=4ZU&t?v zOp8gw1P~o&GFjYCcGBk;58FaKOyk7{M_uiW+()x080Q245uI7x1^WdSX9`3zo`4U3 zj#-)mO)zUi+Kbr;PGZ}CHiXAbWTwZx|DcALT>i#G2`2q=#ctF4T{bNNuwB>BN zYi{u`sV)cUMqRf2Y1L4vaGOhE9<`egoGSm~c6MN-AScM*j_Zy-?xkFzT($>RkHW;z zh!u(3VCb=w30X7c$cu5R3eT};X3bAfPObI1=fvF%5?j!4%$B4)&A@`9XJ;VgZLu!qkl_vzPP3cpI; zzbw85?Bk$oE|u)Lk7fKJGyI=bcz)}{c@$E4VSKl>7ZH=XBTkzBd6U@rVV4fPzmKRX zuMe$tgfkrVOkumT3y@miNAsJULPsWuU^CN(Xj#A&kX)}p;@=%EJ5ROd#(FSLwEB#k zPxY!Lwo#52kx!Yz;#OXp@UpOgJvDib&Q0MD1)$mj9EX^>Dlj3=Wr0n@v9VelL&=>X zj4>0&{arGX1BmvzM&b3NJlg^tgPFNTOqU9`q_BRwfTOd>U`4uwCata`m@rbB)96K8 z5ZNnIBI)PbEAm4@LuZ5aE&2V<*(>2&Z;!Z3*By$K$eKQe3J*w3Smh&I%X~1FH}_iD zA7i3{9|ofL|3z-MkAt+(yU3FSJ4(P5psHM;4TsXPdrA7}C>GxPvj@&=+0B)Z)$2Xl z)0(^0IbrhX{lT>6@p`yMa(|)QG6U}Tlj>c6MR!L6ob07~7@|zOt?SV5)>})Scz;Oa zB&YNIRgd!`$=NqM0asqT>g6()QOSF`TwSH!X_;s88N~ANJ9K1;W{`>UGP{7uk26#? zy8ue^Pbi~%3@qDE7|1~mDGzXeqHL}V&kY<8{ze}`dA6G4Y;rJn-3lU+3yUM@a|WVj zo#vgctKc)%wlL3WHK9-u7sVT0AYyJH{pIszoIn zlwgQsZXtxg82TrnAe5OgIBU5u#mQn{;uq&WKov8h5a9Qxn>u*_%z!p$L}P(Np|m%E zCfXIfMPJdrFPt53-rv>#((Zu2|C%GKoD4>T9=t^G3IYK%9D$dR8`sXkldN*N%8aYA z!3p@E!eMLMX2l8APTts=^Q6R-bIp9caw=E6dm#$~K2O5KO96io8|3#pdJ{~y|4hfD znr$`u%HM5bDlMHSy#PP&w|7BgdioTfiqA*j8n6$kTOmv{QYn?j*{Wb6V(51tL;WvE zb$Gq8c>lGeNCTKo&@{r=K|UyhL_JN<_|IA0eLS+G-%&4gqlX&ixKZB?HtjlePTqwV$Cazp#5qpX*|?Iv#;#{ z5U@}>Q*rM2qvD+E^`#RrOE>Og7b-QtSX5{iHj=(aqVvTgY0oOxEqGb1XXVLy4Nup+ zqK))Up_n1Gw_wP8kNfpDIwMrDehv4~e!UHU-$G~kY5Mnl!1emL?k;TQ51K)i6~*mw zx?BQ69ucy#6sPJC6rWKTXO!zRr9M*!F+ru+i>}jJ5A_hCzQyDjU-}d37q$#+MdvKn zuq^ne_1(e6W6^lfXTgQ^nMdg>u{2~JJsCiZsyAW8bcRSBi*}LGWIMrT69hJz*prj^ zf5rrgF!DI^?P_fti3Xr|)8|elAYJZy`R;VNLJ8_iT?y(P$Rsik0aZt9{7!O$SURwd)W;sNn*s6qkXc});sts?#aRZ90O#$L~RtMqWU3C&Uxk zSoRdhc0xL^guXvef3M9!a%h=c`0w{`X0U0+mFC#+<#JJVs81Bcb*PO2% z(l(uTCNiPz;&aVE?;hwu7c~BIiCDuev!p#o>wB-w!_AI#hV5Voc$x2dcpoOY4k!no zn^_+wQ4jP^)`xck7|Yc6IA0lUfVI*UM!M9Hs_2a7Ow|rLYWY8ksu#;Inm$4 zq?d6U!T}=@MTznpGu-9Gm0Yf(BDLjEU%_Q{+N0V(@B=k8UKSRcv|`k!wPO#|6I;&c z={~EyV1vnu&P6NGv1ps!tUIH`*4OvH@Oqi>OCSg$d2+Q!>Y@Ak3RsgEm#3DN<+uf_ z$5T*Hrk1%?_o&KjkH6H@o-8f3gT16JX?F@%$zqAMBrQ(xW2>Vi<+mgyZgu2m+F=vb z4YfBG6!S+747D<5XklW^&ba%l^Dg;49X9mdMVJ5hJ~HX{#f1<3{$x81AxcgYck33$ z9Xrx-MBUxmH@`5`Vr8K)jfE6lcr&#uX7208==0J|te@p&%p&6Fw2ySs)AIVtKd!wv z`vzrT^V7#bm`~KkkvJQAA6tXQ!!RYHdYCZ8bGiUP{AjR5RDU-uF`$>oa%)hAnzUt$ zGN93EhK(5ErVO)Lo##-7QHNnb2~Zfrm^8y&61_9XfD(67hHB2Bc}ac`%D^nL+W<(( znwLcVu>0uuD*=Wnu^#NK#PW2B%Z8L#{~#@q+uh@M9bkK$v23O9AiOo&I1;skzn#Or zb&_vsJ`Y;jSpCHFBP-38E^Pv?Pm59l_&yWc37i35vYzLy%gb{*=^~w(8N@*{NSX06 zZ3nAt6-1|t=zFIVeXnSBD}pFsH=4*s0$H%RM>LR;gjnB^$6Yk)*M%-+$wLkt(AMu0 zk3(4`hX{7>+&}Q95Jk;;Oww6$2ZUi+qRM5?_U7f8tvL?A!)EqNW+{J!Bj63-2t3Hm z&9dTv>1?Z2al5?6Fr`NaYg0Egxhea=R)Vw?pB=$@?2?cC^!kN6O4_>4e|GOJ>(;Rl zrW)-zWYT`AMGpY{4 zmoWr5LjzoAT;myoyq7aHur)s0$k59f8sVzp7?gI-&^UP*22+GHG{dX9pO8@ua_r+A z&6|ND7)hFCONKvkG79M|u}l`(&McJ-1GfOM^j`YC zXrbTJj4ia>IyTZ#KYRXC{rOoO7A-ZYBjR8jc}6cNJ=ro2Kjko5NZD*5QHDYz!}>G> zbU}sw^kM$=G)D<7WxPJCt!AZgi(wT;m}n^r*{s$M^g1ntGi+lF%(lY4wVXkGU8$iA z6am)(P=t|#*8hxqVD2tZB zs8OaWQ8v+pJMKE-CBc7|K``6-R`v6)x*-9GT((2MU>Q5rIAVz^Dpz|gMm3k-rS?JwdaaPjVg*^%LvHK z>aF(E>?>tZ3ypAEB9TtPQc_S*U0!~~#OjG7Os0w1Vsld&o{h&PD(maLMgLuHmh4LM z&VnyuAqn^S(*HPuz9D_`H>7Nd9VTT2ZsR?~0^U*r#66(_~4*b6kX@yC$kH2i?|%FoGr zK?F}f0+3`7feFW*&yZ+3U*rej<$;m%UYf;oKSuwiw9}`vf=yLa)$UNhB9dy*TF}mi z>%j!aads{if3v*OfBy^S1Kz1ij!Jrz_8Xsm=&tEClbQ%;xAZ#Alwrm-Pyj4hq6xDN zRsBW_x>;@td0RRY28u}s#RFK=Yyp0Kf^Mgw-xd766yqCa6?ROv_-qs(?2m0H-#t zD8Ec^89i&ksk=KCADl7vsYd^sxjn}=Z)s?o1AyQ*T=UX;HjBI}xZ`Nbh`Nks(KQO>KJeWZ{{iQO20L|Jt^3C7l^PH$6~paK2X60xlo?)OR8$Kx${C^i$!&a z`C_Fgia{p{vRG7SjLbqhZ){B3i9YxVy-+Bkc=c&FY1drVJa=yMWx_GNt(~2%z4DHK zCcPUT%Cc=oix(&F(>O!n^=D2_0bIDAlPbfm0C|_HOSYNGoiF~W7@A{|* zSAjx)^8NqT-UL3X>g*ps&s}HB>}$5kWHMO@Nk~EnVI8&r0U>OGu!)GsDw~Li6cI5f zA}Y8P5iKfGioDb!Dpe~WDk4Ry)bc7tv`DdPty>i`hyQc#Ju{h^fN%Tz{K+R|W^!}x zbIx<_InQ~P?^n`dixCpX8J^3qTbZTAC~p*17Bp02j_1h~m^e3KJ^8D2k-V4gzj&9t zm$etd_EyVS!zB0U<8X@hf74Ya$|Y_F`zV|p`cbFq59{@NszL3Z6>XkZ?R_V092;?*2XWps zGwB`869wD>Op4KjDXkh7FEL3bGy27xl8OnD;+27x>vSeKk(VJI5rtMdQry-$UOd)n z5I4|+;uLzY^)P)E`Z7tl22qmA_>7W=YB-+Zs6�$i!e&Fd;mFJk0Nwf}bo8r3>j? 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 yJH2HDD1wZV`Kkgq^;y%6Ku?f&UlXX*7b i18nService.translationLocale, + deps: [I18nServiceAbstraction], + }, + ValidationService, + AuthGuardService, + UnauthGuardService, + LockGuardService, + ModalService, + { + provide: AppIdServiceAbstraction, + useClass: AppIdService, + deps: [StorageServiceAbstraction], + }, + { + provide: AuditServiceAbstraction, + useClass: AuditService, + deps: [CryptoFunctionServiceAbstraction, ApiServiceAbstraction], + }, + { + provide: AuthServiceAbstraction, + useClass: AuthService, + deps: [ + CryptoServiceAbstraction, + ApiServiceAbstraction, + TokenServiceAbstraction, + AppIdServiceAbstraction, + PlatformUtilsServiceAbstraction, + MessagingServiceAbstraction, + LogService, + KeyConnectorServiceAbstraction, + EnvironmentServiceAbstraction, + StateServiceAbstraction, + TwoFactorServiceAbstraction, + I18nServiceAbstraction, + ], + }, + { + provide: CipherServiceAbstraction, + useFactory: ( + cryptoService: CryptoServiceAbstraction, + settingsService: SettingsServiceAbstraction, + apiService: ApiServiceAbstraction, + fileUploadService: FileUploadServiceAbstraction, + i18nService: I18nServiceAbstraction, + injector: Injector, + logService: LogService, + stateService: StateServiceAbstraction + ) => + new CipherService( + cryptoService, + settingsService, + apiService, + fileUploadService, + i18nService, + () => injector.get(SearchServiceAbstraction), + logService, + stateService + ), + deps: [ + CryptoServiceAbstraction, + SettingsServiceAbstraction, + ApiServiceAbstraction, + FileUploadServiceAbstraction, + I18nServiceAbstraction, + Injector, // TODO: Get rid of this circular dependency! + LogService, + StateServiceAbstraction, + ], + }, + { + provide: FolderServiceAbstraction, + useClass: FolderService, + deps: [ + CryptoServiceAbstraction, + ApiServiceAbstraction, + I18nServiceAbstraction, + CipherServiceAbstraction, + StateServiceAbstraction, + ], + }, + { provide: LogService, useFactory: () => new ConsoleLogService(false) }, + { + provide: CollectionServiceAbstraction, + useClass: CollectionService, + deps: [CryptoServiceAbstraction, I18nServiceAbstraction, StateServiceAbstraction], + }, + { + provide: EnvironmentServiceAbstraction, + useClass: EnvironmentService, + deps: [StateServiceAbstraction], + }, + { + provide: TotpServiceAbstraction, + useClass: TotpService, + deps: [CryptoFunctionServiceAbstraction, LogService, StateServiceAbstraction], + }, + { provide: TokenServiceAbstraction, useClass: TokenService, deps: [StateServiceAbstraction] }, + { + provide: CryptoServiceAbstraction, + useClass: CryptoService, + deps: [ + CryptoFunctionServiceAbstraction, + PlatformUtilsServiceAbstraction, + LogService, + StateServiceAbstraction, + ], + }, + { + provide: PasswordGenerationServiceAbstraction, + useClass: PasswordGenerationService, + deps: [CryptoServiceAbstraction, PolicyServiceAbstraction, StateServiceAbstraction], + }, + { + provide: UsernameGenerationServiceAbstraction, + useClass: UsernameGenerationService, + deps: [CryptoServiceAbstraction, StateServiceAbstraction], + }, + { + provide: ApiServiceAbstraction, + useFactory: ( + tokenService: TokenServiceAbstraction, + platformUtilsService: PlatformUtilsServiceAbstraction, + environmentService: EnvironmentServiceAbstraction, + messagingService: MessagingServiceAbstraction, + appIdService: AppIdServiceAbstraction + ) => + new ApiService( + tokenService, + platformUtilsService, + environmentService, + appIdService, + async (expired: boolean) => messagingService.send("logout", { expired: expired }) + ), + deps: [ + TokenServiceAbstraction, + PlatformUtilsServiceAbstraction, + EnvironmentServiceAbstraction, + MessagingServiceAbstraction, + AppIdServiceAbstraction, + ], + }, + { + provide: FileUploadServiceAbstraction, + useClass: FileUploadService, + deps: [LogService, ApiServiceAbstraction], + }, + { + provide: SyncServiceAbstraction, + useFactory: ( + apiService: ApiServiceAbstraction, + settingsService: SettingsServiceAbstraction, + folderService: FolderServiceAbstraction, + cipherService: CipherServiceAbstraction, + cryptoService: CryptoServiceAbstraction, + collectionService: CollectionServiceAbstraction, + messagingService: MessagingServiceAbstraction, + policyService: PolicyServiceAbstraction, + sendService: SendServiceAbstraction, + logService: LogService, + keyConnectorService: KeyConnectorServiceAbstraction, + stateService: StateServiceAbstraction, + organizationService: OrganizationServiceAbstraction, + providerService: ProviderServiceAbstraction + ) => + new SyncService( + apiService, + settingsService, + folderService, + cipherService, + cryptoService, + collectionService, + messagingService, + policyService, + sendService, + logService, + keyConnectorService, + stateService, + organizationService, + providerService, + async (expired: boolean) => messagingService.send("logout", { expired: expired }) + ), + deps: [ + ApiServiceAbstraction, + SettingsServiceAbstraction, + FolderServiceAbstraction, + CipherServiceAbstraction, + CryptoServiceAbstraction, + CollectionServiceAbstraction, + MessagingServiceAbstraction, + PolicyServiceAbstraction, + SendServiceAbstraction, + LogService, + KeyConnectorServiceAbstraction, + StateServiceAbstraction, + OrganizationServiceAbstraction, + ProviderServiceAbstraction, + ], + }, + { provide: BroadcasterServiceAbstraction, useClass: BroadcasterService }, + { + provide: SettingsServiceAbstraction, + useClass: SettingsService, + deps: [StateServiceAbstraction], + }, + { + provide: VaultTimeoutServiceAbstraction, + useFactory: ( + cipherService: CipherServiceAbstraction, + folderService: FolderServiceAbstraction, + collectionService: CollectionServiceAbstraction, + cryptoService: CryptoServiceAbstraction, + platformUtilsService: PlatformUtilsServiceAbstraction, + messagingService: MessagingServiceAbstraction, + searchService: SearchServiceAbstraction, + tokenService: TokenServiceAbstraction, + policyService: PolicyServiceAbstraction, + keyConnectorService: KeyConnectorServiceAbstraction, + stateService: StateServiceAbstraction + ) => + new VaultTimeoutService( + cipherService, + folderService, + collectionService, + cryptoService, + platformUtilsService, + messagingService, + searchService, + tokenService, + policyService, + keyConnectorService, + stateService, + null, + async (userId?: string) => + messagingService.send("logout", { expired: false, userId: userId }) + ), + deps: [ + CipherServiceAbstraction, + FolderServiceAbstraction, + CollectionServiceAbstraction, + CryptoServiceAbstraction, + PlatformUtilsServiceAbstraction, + MessagingServiceAbstraction, + SearchServiceAbstraction, + TokenServiceAbstraction, + PolicyServiceAbstraction, + KeyConnectorServiceAbstraction, + StateServiceAbstraction, + ], + }, + { + provide: StateServiceAbstraction, + useFactory: ( + storageService: StorageServiceAbstraction, + secureStorageService: StorageServiceAbstraction, + logService: LogService, + stateMigrationService: StateMigrationServiceAbstraction + ) => + new StateService( + storageService, + secureStorageService, + logService, + stateMigrationService, + new StateFactory(GlobalState, Account) + ), + deps: [ + StorageServiceAbstraction, + "SECURE_STORAGE", + LogService, + StateMigrationServiceAbstraction, + ], + }, + { + provide: StateMigrationServiceAbstraction, + useFactory: ( + storageService: StorageServiceAbstraction, + secureStorageService: StorageServiceAbstraction + ) => + new StateMigrationService( + storageService, + secureStorageService, + new StateFactory(GlobalState, Account) + ), + deps: [StorageServiceAbstraction, "SECURE_STORAGE"], + }, + { + provide: ExportServiceAbstraction, + useClass: ExportService, + deps: [ + FolderServiceAbstraction, + CipherServiceAbstraction, + ApiServiceAbstraction, + CryptoServiceAbstraction, + ], + }, + { + provide: SearchServiceAbstraction, + useClass: SearchService, + deps: [CipherServiceAbstraction, LogService, I18nServiceAbstraction], + }, + { + provide: NotificationsServiceAbstraction, + useFactory: ( + syncService: SyncServiceAbstraction, + appIdService: AppIdServiceAbstraction, + apiService: ApiServiceAbstraction, + vaultTimeoutService: VaultTimeoutServiceAbstraction, + environmentService: EnvironmentServiceAbstraction, + messagingService: MessagingServiceAbstraction, + logService: LogService, + stateService: StateServiceAbstraction + ) => + new NotificationsService( + syncService, + appIdService, + apiService, + vaultTimeoutService, + environmentService, + async () => messagingService.send("logout", { expired: true }), + logService, + stateService + ), + deps: [ + SyncServiceAbstraction, + AppIdServiceAbstraction, + ApiServiceAbstraction, + VaultTimeoutServiceAbstraction, + EnvironmentServiceAbstraction, + MessagingServiceAbstraction, + LogService, + StateServiceAbstraction, + ], + }, + { + provide: CryptoFunctionServiceAbstraction, + useClass: WebCryptoFunctionService, + deps: ["WINDOW"], + }, + { + provide: EventServiceAbstraction, + useClass: EventService, + deps: [ + ApiServiceAbstraction, + CipherServiceAbstraction, + StateServiceAbstraction, + LogService, + OrganizationServiceAbstraction, + ], + }, + { + provide: PolicyServiceAbstraction, + useClass: PolicyService, + deps: [StateServiceAbstraction, OrganizationServiceAbstraction, ApiServiceAbstraction], + }, + { + provide: SendServiceAbstraction, + useClass: SendService, + deps: [ + CryptoServiceAbstraction, + ApiServiceAbstraction, + FileUploadServiceAbstraction, + I18nServiceAbstraction, + CryptoFunctionServiceAbstraction, + StateServiceAbstraction, + ], + }, + { + provide: KeyConnectorServiceAbstraction, + useClass: KeyConnectorService, + deps: [ + StateServiceAbstraction, + CryptoServiceAbstraction, + ApiServiceAbstraction, + TokenServiceAbstraction, + LogService, + OrganizationServiceAbstraction, + CryptoFunctionServiceAbstraction, + ], + }, + { + provide: UserVerificationServiceAbstraction, + useClass: UserVerificationService, + deps: [CryptoServiceAbstraction, I18nServiceAbstraction, ApiServiceAbstraction], + }, + { provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService }, + { + provide: OrganizationServiceAbstraction, + useClass: OrganizationService, + deps: [StateServiceAbstraction], + }, + { + provide: ProviderServiceAbstraction, + useClass: ProviderService, + deps: [StateServiceAbstraction], + }, + { + provide: TwoFactorServiceAbstraction, + useClass: TwoFactorService, + deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction], + }, + ], +}) +export class JslibServicesModule {} diff --git a/jslib/angular/src/services/lock-guard.service.ts b/jslib/angular/src/services/lock-guard.service.ts new file mode 100644 index 00000000..992c6e5d --- /dev/null +++ b/jslib/angular/src/services/lock-guard.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from "@angular/core"; +import { CanActivate, Router } from "@angular/router"; + +import { StateService } from "jslib-common/abstractions/state.service"; +import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; + +@Injectable() +export class LockGuardService implements CanActivate { + protected homepage = "vault"; + protected loginpage = "login"; + constructor( + private vaultTimeoutService: VaultTimeoutService, + private router: Router, + private stateService: StateService + ) {} + + async canActivate() { + if (await this.vaultTimeoutService.isLocked()) { + return true; + } + + const redirectUrl = (await this.stateService.getIsAuthenticated()) + ? [this.homepage] + : [this.loginpage]; + + this.router.navigate(redirectUrl); + return false; + } +} diff --git a/jslib/angular/src/services/modal.service.ts b/jslib/angular/src/services/modal.service.ts new file mode 100644 index 00000000..aecc84c6 --- /dev/null +++ b/jslib/angular/src/services/modal.service.ts @@ -0,0 +1,180 @@ +import { + ApplicationRef, + ComponentFactory, + ComponentFactoryResolver, + ComponentRef, + EmbeddedViewRef, + Injectable, + Injector, + Type, + ViewContainerRef, +} from "@angular/core"; +import { first } from "rxjs/operators"; + +import { DynamicModalComponent } from "../components/modal/dynamic-modal.component"; +import { ModalInjector } from "../components/modal/modal-injector"; +import { ModalRef } from "../components/modal/modal.ref"; + +export class ModalConfig { + data?: D; + allowMultipleModals = false; +} + +@Injectable() +export class ModalService { + protected modalList: ComponentRef[] = []; + + // Lazy loaded modules are not available in componentFactoryResolver, + // therefore modules needs to manually initialize their resolvers. + private factoryResolvers: Map, ComponentFactoryResolver> = new Map(); + + constructor( + private componentFactoryResolver: ComponentFactoryResolver, + private applicationRef: ApplicationRef, + private injector: Injector + ) { + document.addEventListener("keyup", (event) => { + if (event.key === "Escape" && this.modalCount > 0) { + this.topModal.instance.close(); + } + }); + } + + get modalCount() { + return this.modalList.length; + } + + private get topModal() { + return this.modalList[this.modalCount - 1]; + } + + async openViewRef( + componentType: Type, + viewContainerRef: ViewContainerRef, + setComponentParameters: (component: T) => void = null + ): Promise<[ModalRef, T]> { + const [modalRef, modalComponentRef] = this.openInternal(componentType, null, false); + modalComponentRef.instance.setComponentParameters = setComponentParameters; + + viewContainerRef.insert(modalComponentRef.hostView); + + await modalRef.onCreated.pipe(first()).toPromise(); + + return [modalRef, modalComponentRef.instance.componentRef.instance]; + } + + open(componentType: Type, config?: ModalConfig) { + if (!(config?.allowMultipleModals ?? false) && this.modalCount > 0) { + return; + } + + // eslint-disable-next-line + const [modalRef, _] = this.openInternal(componentType, config, true); + + return modalRef; + } + + registerComponentFactoryResolver( + componentType: Type, + componentFactoryResolver: ComponentFactoryResolver + ): void { + this.factoryResolvers.set(componentType, componentFactoryResolver); + } + + resolveComponentFactory(componentType: Type): ComponentFactory { + if (this.factoryResolvers.has(componentType)) { + return this.factoryResolvers.get(componentType).resolveComponentFactory(componentType); + } + + return this.componentFactoryResolver.resolveComponentFactory(componentType); + } + + protected openInternal( + componentType: Type, + config?: ModalConfig, + attachToDom?: boolean + ): [ModalRef, ComponentRef] { + const [modalRef, componentRef] = this.createModalComponent(config); + componentRef.instance.childComponentType = componentType; + + if (attachToDom) { + this.applicationRef.attachView(componentRef.hostView); + const domElem = (componentRef.hostView as EmbeddedViewRef).rootNodes[0] as HTMLElement; + document.body.appendChild(domElem); + } + + modalRef.onClosed.pipe(first()).subscribe(() => { + if (attachToDom) { + this.applicationRef.detachView(componentRef.hostView); + } + componentRef.destroy(); + + this.modalList.pop(); + if (this.modalCount > 0) { + this.topModal.instance.getFocus(); + } + }); + + this.setupHandlers(modalRef); + + this.modalList.push(componentRef); + + return [modalRef, componentRef]; + } + + protected setupHandlers(modalRef: ModalRef) { + let backdrop: HTMLElement = null; + + // Add backdrop, setup [data-dismiss] handler. + modalRef.onCreated.pipe(first()).subscribe((el) => { + document.body.classList.add("modal-open"); + + const modalEl: HTMLElement = el.querySelector(".modal"); + const dialogEl = modalEl.querySelector(".modal-dialog") as HTMLElement; + + backdrop = document.createElement("div"); + backdrop.className = "modal-backdrop fade"; + backdrop.style.zIndex = `${this.modalCount}040`; + modalEl.prepend(backdrop); + + dialogEl.addEventListener("click", (e: Event) => { + e.stopPropagation(); + }); + dialogEl.style.zIndex = `${this.modalCount}050`; + + const modals = Array.from( + el.querySelectorAll('.modal-backdrop, .modal *[data-dismiss="modal"]') + ); + for (const closeElement of modals) { + closeElement.addEventListener("click", () => { + modalRef.close(); + }); + } + }); + + // onClose is used in Web to hook into bootstrap. On other projects we pipe it directly to closed. + modalRef.onClose.pipe(first()).subscribe(() => { + modalRef.closed(); + + if (this.modalCount === 0) { + document.body.classList.remove("modal-open"); + } + }); + } + + protected createModalComponent( + config: ModalConfig + ): [ModalRef, ComponentRef] { + const modalRef = new ModalRef(); + + const map = new WeakMap(); + map.set(ModalConfig, config); + map.set(ModalRef, modalRef); + + const componentFactory = + this.componentFactoryResolver.resolveComponentFactory(DynamicModalComponent); + const componentRef = componentFactory.create(new ModalInjector(this.injector, map)); + + return [modalRef, componentRef]; + } +} diff --git a/jslib/angular/src/services/passwordReprompt.service.ts b/jslib/angular/src/services/passwordReprompt.service.ts new file mode 100644 index 00000000..723c41f7 --- /dev/null +++ b/jslib/angular/src/services/passwordReprompt.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from "@angular/core"; + +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service"; + +import { PasswordRepromptComponent } from "../components/password-reprompt.component"; + +import { ModalService } from "./modal.service"; + +/** + * Used to verify the user's Master Password for the "Master Password Re-prompt" feature only. + * See UserVerificationService for any other situation where you need to verify the user's identity. + */ +@Injectable() +export class PasswordRepromptService implements PasswordRepromptServiceAbstraction { + protected component = PasswordRepromptComponent; + + constructor( + private modalService: ModalService, + private keyConnectorService: KeyConnectorService + ) {} + + protectedFields() { + return ["TOTP", "Password", "H_Field", "Card Number", "Security Code"]; + } + + async showPasswordPrompt() { + if (!(await this.enabled())) { + return true; + } + + const ref = this.modalService.open(this.component, { allowMultipleModals: true }); + + if (ref == null) { + return false; + } + + const result = await ref.onClosedPromise(); + return result === true; + } + + async enabled() { + return !(await this.keyConnectorService.getUsesKeyConnector()); + } +} diff --git a/jslib/angular/src/services/unauth-guard.service.ts b/jslib/angular/src/services/unauth-guard.service.ts new file mode 100644 index 00000000..ee2da045 --- /dev/null +++ b/jslib/angular/src/services/unauth-guard.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from "@angular/core"; +import { CanActivate, Router } from "@angular/router"; + +import { StateService } from "jslib-common/abstractions/state.service"; +import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; + +@Injectable() +export class UnauthGuardService implements CanActivate { + protected homepage = "vault"; + constructor( + private vaultTimeoutService: VaultTimeoutService, + private router: Router, + private stateService: StateService + ) {} + + async canActivate() { + const isAuthed = await this.stateService.getIsAuthenticated(); + if (isAuthed) { + const locked = await this.vaultTimeoutService.isLocked(); + if (locked) { + this.router.navigate(["lock"]); + } else { + this.router.navigate([this.homepage]); + } + return false; + } + return true; + } +} diff --git a/jslib/angular/src/services/validation.service.ts b/jslib/angular/src/services/validation.service.ts new file mode 100644 index 00000000..865f6458 --- /dev/null +++ b/jslib/angular/src/services/validation.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from "@angular/core"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { ErrorResponse } from "jslib-common/models/response/errorResponse"; + +@Injectable() +export class ValidationService { + constructor( + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService + ) {} + + showError(data: any): string[] { + const defaultErrorMessage = this.i18nService.t("unexpectedError"); + let errors: string[] = []; + + if (data != null && typeof data === "string") { + errors.push(data); + } else if (data == null || typeof data !== "object") { + errors.push(defaultErrorMessage); + } else if (data.validationErrors != null) { + errors = errors.concat((data as ErrorResponse).getAllMessages()); + } else { + errors.push(data.message ? data.message : defaultErrorMessage); + } + + if (errors.length === 1) { + this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errors[0]); + } else if (errors.length > 1) { + this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errors, { + timeout: 5000 * errors.length, + }); + } + + return errors; + } +} diff --git a/jslib/angular/tsconfig.json b/jslib/angular/tsconfig.json new file mode 100644 index 00000000..c2fd662c --- /dev/null +++ b/jslib/angular/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../shared/tsconfig", + "compilerOptions": { + "paths": { + "jslib-common/*": ["../common/src/*"], + "jslib-angular/*": ["./src/"] + } + }, + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/angular/tsconfig.spec.json b/jslib/angular/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/angular/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/jslib/common/jest.config.js b/jslib/common/jest.config.js new file mode 100644 index 00000000..76856611 --- /dev/null +++ b/jslib/common/jest.config.js @@ -0,0 +1,18 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + name: "common", + displayName: "common jslib tests", + preset: "ts-jest", + testEnvironment: "jsdom", + testMatch: ["**/+(*.)+(spec).+(ts)"], + setupFilesAfterEnv: ["/spec/test.ts"], + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/jslib/common/package-lock.json b/jslib/common/package-lock.json new file mode 100644 index 00000000..2bf7f067 --- /dev/null +++ b/jslib/common/package-lock.json @@ -0,0 +1,956 @@ +{ + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@microsoft/signalr": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz", + "integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.7.3", + "node-fetch": "^2.6.0", + "ws": "^6.0.0" + } + }, + "node_modules/@microsoft/signalr-protocol-msgpack": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz", + "integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==", + "dependencies": { + "@microsoft/signalr": ">=5.0.10", + "msgpack5": "^4.5.0" + } + }, + "node_modules/@types/lunr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", + "integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "node_modules/@types/node-forge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==", + "dev": true + }, + "node_modules/@types/zxcvbn": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz", + "integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browser-hrtime": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", + "integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/es6-denodeify": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz", + "integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8=" + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "dependencies": { + "original": "^1.0.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/fetch-cookie": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz", + "integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==", + "dependencies": { + "es6-denodeify": "^0.1.1", + "tough-cookie": "^2.3.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/msgpack5": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", + "integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==", + "dependencies": { + "bl": "^2.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.3.6", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dependencies": { + "url-parse": "^1.4.3" + } + }, + "node_modules/papaparse": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz", + "integrity": "sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA==" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "hasInstallScript": true, + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tough-cookie/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + } + }, + "dependencies": { + "@microsoft/signalr": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz", + "integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==", + "requires": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.7.3", + "node-fetch": "^2.6.0", + "ws": "^6.0.0" + } + }, + "@microsoft/signalr-protocol-msgpack": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz", + "integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==", + "requires": { + "@microsoft/signalr": ">=5.0.10", + "msgpack5": "^4.5.0" + } + }, + "@types/lunr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", + "integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==", + "dev": true + }, + "@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "@types/node-forge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==", + "dev": true + }, + "@types/zxcvbn": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz", + "integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==", + "dev": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" + }, + "bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-hrtime": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", + "integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "es6-denodeify": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz", + "integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8=" + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "requires": { + "original": "^1.0.0" + } + }, + "fetch-cookie": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz", + "integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==", + "requires": { + "es6-denodeify": "^0.1.1", + "tough-cookie": "^2.3.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "msgpack5": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", + "integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==", + "requires": { + "bl": "^2.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.3.6", + "safe-buffer": "^5.1.2" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "papaparse": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz", + "integrity": "sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "requires": { + "punycode": "^1.4.1" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + } + } +} diff --git a/jslib/common/package.json b/jslib/common/package.json new file mode 100644 index 00000000..14df9a75 --- /dev/null +++ b/jslib/common/package.json @@ -0,0 +1,42 @@ +{ + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + } +} diff --git a/jslib/common/spec/domain/attachment.spec.ts b/jslib/common/spec/domain/attachment.spec.ts new file mode 100644 index 00000000..f625adbf --- /dev/null +++ b/jslib/common/spec/domain/attachment.spec.ts @@ -0,0 +1,83 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { AttachmentData } from "jslib-common/models/data/attachmentData"; +import { Attachment } from "jslib-common/models/domain/attachment"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { ContainerService } from "jslib-common/services/container.service"; + +import { makeStaticByteArray, mockEnc } from "../utils"; + +describe("Attachment", () => { + let data: AttachmentData; + + beforeEach(() => { + data = { + id: "id", + url: "url", + fileName: "fileName", + key: "key", + size: "1100", + sizeName: "1.1 KB", + }; + }); + + it("Convert from empty", () => { + const data = new AttachmentData(); + const attachment = new Attachment(data); + + expect(attachment).toEqual({ + id: null, + url: null, + size: undefined, + sizeName: null, + key: null, + fileName: null, + }); + }); + + it("Convert", () => { + const attachment = new Attachment(data); + + expect(attachment).toEqual({ + size: "1100", + id: "id", + url: "url", + sizeName: "1.1 KB", + fileName: { encryptedString: "fileName", encryptionType: 0 }, + key: { encryptedString: "key", encryptionType: 0 }, + }); + }); + + it("toAttachmentData", () => { + const attachment = new Attachment(data); + expect(attachment.toAttachmentData()).toEqual(data); + }); + + it("Decrypt", async () => { + const attachment = new Attachment(); + attachment.id = "id"; + attachment.url = "url"; + attachment.size = "1100"; + attachment.sizeName = "1.1 KB"; + attachment.key = mockEnc("key"); + attachment.fileName = mockEnc("fileName"); + + const cryptoService = Substitute.for(); + cryptoService.getOrgKey(null).resolves(null); + cryptoService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(32)); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + + const view = await attachment.decrypt(null); + + expect(view).toEqual({ + id: "id", + url: "url", + size: "1100", + sizeName: "1.1 KB", + fileName: "fileName", + key: expect.any(SymmetricCryptoKey), + }); + }); +}); diff --git a/jslib/common/spec/domain/card.spec.ts b/jslib/common/spec/domain/card.spec.ts new file mode 100644 index 00000000..f4709bcc --- /dev/null +++ b/jslib/common/spec/domain/card.spec.ts @@ -0,0 +1,73 @@ +import { CardData } from "jslib-common/models/data/cardData"; +import { Card } from "jslib-common/models/domain/card"; + +import { mockEnc } from "../utils"; + +describe("Card", () => { + let data: CardData; + + beforeEach(() => { + data = { + cardholderName: "encHolder", + brand: "encBrand", + number: "encNumber", + expMonth: "encMonth", + expYear: "encYear", + code: "encCode", + }; + }); + + it("Convert from empty", () => { + const data = new CardData(); + const card = new Card(data); + + expect(card).toEqual({ + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }); + }); + + it("Convert", () => { + const card = new Card(data); + + expect(card).toEqual({ + cardholderName: { encryptedString: "encHolder", encryptionType: 0 }, + brand: { encryptedString: "encBrand", encryptionType: 0 }, + number: { encryptedString: "encNumber", encryptionType: 0 }, + expMonth: { encryptedString: "encMonth", encryptionType: 0 }, + expYear: { encryptedString: "encYear", encryptionType: 0 }, + code: { encryptedString: "encCode", encryptionType: 0 }, + }); + }); + + it("toCardData", () => { + const card = new Card(data); + expect(card.toCardData()).toEqual(data); + }); + + it("Decrypt", async () => { + const card = new Card(); + card.cardholderName = mockEnc("cardHolder"); + card.brand = mockEnc("brand"); + card.number = mockEnc("number"); + card.expMonth = mockEnc("expMonth"); + card.expYear = mockEnc("expYear"); + card.code = mockEnc("code"); + + const view = await card.decrypt(null); + + expect(view).toEqual({ + _brand: "brand", + _number: "number", + _subTitle: null, + cardholderName: "cardHolder", + code: "code", + expMonth: "expMonth", + expYear: "expYear", + }); + }); +}); diff --git a/jslib/common/spec/domain/cipher.spec.ts b/jslib/common/spec/domain/cipher.spec.ts new file mode 100644 index 00000000..0e6ed452 --- /dev/null +++ b/jslib/common/spec/domain/cipher.spec.ts @@ -0,0 +1,599 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType"; +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { UriMatchType } from "jslib-common/enums/uriMatchType"; +import { CipherData } from "jslib-common/models/data/cipherData"; +import { Card } from "jslib-common/models/domain/card"; +import { Cipher } from "jslib-common/models/domain/cipher"; +import { Identity } from "jslib-common/models/domain/identity"; +import { Login } from "jslib-common/models/domain/login"; +import { SecureNote } from "jslib-common/models/domain/secureNote"; +import { CardView } from "jslib-common/models/view/cardView"; +import { IdentityView } from "jslib-common/models/view/identityView"; +import { LoginView } from "jslib-common/models/view/loginView"; + +import { mockEnc } from "../utils"; + +describe("Cipher DTO", () => { + it("Convert from empty CipherData", () => { + const data = new CipherData(); + const cipher = new Cipher(data); + + expect(cipher).toEqual({ + id: null, + userId: null, + organizationId: null, + folderId: null, + name: null, + notes: null, + type: undefined, + favorite: undefined, + organizationUseTotp: undefined, + edit: undefined, + viewPassword: true, + revisionDate: null, + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: undefined, + attachments: null, + fields: null, + passwordHistory: null, + }); + }); + + describe("LoginCipher", () => { + let cipherData: CipherData; + + beforeEach(() => { + cipherData = { + id: "id", + organizationId: "orgId", + folderId: "folderId", + userId: "userId", + edit: true, + viewPassword: true, + organizationUseTotp: true, + favorite: false, + revisionDate: "2022-01-31T12:00:00.000Z", + type: CipherType.Login, + name: "EncryptedString", + notes: "EncryptedString", + deletedDate: null, + reprompt: CipherRepromptType.None, + login: { + uris: [{ uri: "EncryptedString", match: UriMatchType.Domain }], + username: "EncryptedString", + password: "EncryptedString", + passwordRevisionDate: "2022-01-31T12:00:00.000Z", + totp: "EncryptedString", + autofillOnPageLoad: false, + }, + passwordHistory: [ + { password: "EncryptedString", lastUsedDate: "2022-01-31T12:00:00.000Z" }, + ], + attachments: [ + { + id: "a1", + url: "url", + size: "1100", + sizeName: "1.1 KB", + fileName: "file", + key: "EncKey", + }, + { + id: "a2", + url: "url", + size: "1100", + sizeName: "1.1 KB", + fileName: "file", + key: "EncKey", + }, + ], + fields: [ + { + name: "EncryptedString", + value: "EncryptedString", + type: FieldType.Text, + linkedId: null, + }, + { + name: "EncryptedString", + value: "EncryptedString", + type: FieldType.Hidden, + linkedId: null, + }, + ], + }; + }); + + it("Convert", () => { + const cipher = new Cipher(cipherData); + + expect(cipher).toEqual({ + id: "id", + userId: "userId", + organizationId: "orgId", + folderId: "folderId", + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + notes: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 1, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: 0, + login: { + passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"), + autofillOnPageLoad: false, + username: { encryptedString: "EncryptedString", encryptionType: 0 }, + password: { encryptedString: "EncryptedString", encryptionType: 0 }, + totp: { encryptedString: "EncryptedString", encryptionType: 0 }, + uris: [{ match: 0, uri: { encryptedString: "EncryptedString", encryptionType: 0 } }], + }, + attachments: [ + { + fileName: { encryptedString: "file", encryptionType: 0 }, + id: "a1", + key: { encryptedString: "EncKey", encryptionType: 0 }, + size: "1100", + sizeName: "1.1 KB", + url: "url", + }, + { + fileName: { encryptedString: "file", encryptionType: 0 }, + id: "a2", + key: { encryptedString: "EncKey", encryptionType: 0 }, + size: "1100", + sizeName: "1.1 KB", + url: "url", + }, + ], + fields: [ + { + linkedId: null, + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 0, + value: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + { + linkedId: null, + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 1, + value: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + ], + passwordHistory: [ + { + lastUsedDate: new Date("2022-01-31T12:00:00.000Z"), + password: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + ], + }); + }); + + it("toCipherData", () => { + const cipher = new Cipher(cipherData); + expect(cipher.toCipherData("userId")).toEqual(cipherData); + }); + + it("Decrypt", async () => { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.organizationId = "orgId"; + cipher.folderId = "folderId"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.organizationUseTotp = true; + cipher.favorite = false; + cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + cipher.type = CipherType.Login; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.deletedDate = null; + cipher.reprompt = CipherRepromptType.None; + + const loginView = new LoginView(); + loginView.username = "username"; + loginView.password = "password"; + + const login = Substitute.for(); + login.decrypt(Arg.any(), Arg.any()).resolves(loginView); + cipher.login = login; + + const cipherView = await cipher.decrypt(); + + expect(cipherView).toMatchObject({ + id: "id", + organizationId: "orgId", + folderId: "folderId", + name: "EncryptedString", + notes: "EncryptedString", + type: 1, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + login: loginView, + attachments: null, + fields: null, + passwordHistory: null, + collectionIds: undefined, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + deletedDate: null, + reprompt: 0, + localData: undefined, + }); + }); + }); + + describe("SecureNoteCipher", () => { + let cipherData: CipherData; + + beforeEach(() => { + cipherData = { + id: "id", + organizationId: "orgId", + folderId: "folderId", + userId: "userId", + edit: true, + viewPassword: true, + organizationUseTotp: true, + favorite: false, + revisionDate: "2022-01-31T12:00:00.000Z", + type: CipherType.SecureNote, + name: "EncryptedString", + notes: "EncryptedString", + deletedDate: null, + reprompt: CipherRepromptType.None, + secureNote: { + type: SecureNoteType.Generic, + }, + }; + }); + + it("Convert", () => { + const cipher = new Cipher(cipherData); + + expect(cipher).toEqual({ + id: "id", + userId: "userId", + organizationId: "orgId", + folderId: "folderId", + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + notes: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 2, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: 0, + secureNote: { type: SecureNoteType.Generic }, + attachments: null, + fields: null, + passwordHistory: null, + }); + }); + + it("toCipherData", () => { + const cipher = new Cipher(cipherData); + expect(cipher.toCipherData("userId")).toEqual(cipherData); + }); + + it("Decrypt", async () => { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.organizationId = "orgId"; + cipher.folderId = "folderId"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.organizationUseTotp = true; + cipher.favorite = false; + cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + cipher.type = CipherType.SecureNote; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.deletedDate = null; + cipher.reprompt = CipherRepromptType.None; + cipher.secureNote = new SecureNote(); + cipher.secureNote.type = SecureNoteType.Generic; + + const cipherView = await cipher.decrypt(); + + expect(cipherView).toMatchObject({ + id: "id", + organizationId: "orgId", + folderId: "folderId", + name: "EncryptedString", + notes: "EncryptedString", + type: 2, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + secureNote: { type: 0 }, + attachments: null, + fields: null, + passwordHistory: null, + collectionIds: undefined, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + deletedDate: null, + reprompt: 0, + localData: undefined, + }); + }); + }); + + describe("CardCipher", () => { + let cipherData: CipherData; + + beforeEach(() => { + cipherData = { + id: "id", + organizationId: "orgId", + folderId: "folderId", + userId: "userId", + edit: true, + viewPassword: true, + organizationUseTotp: true, + favorite: false, + revisionDate: "2022-01-31T12:00:00.000Z", + type: CipherType.Card, + name: "EncryptedString", + notes: "EncryptedString", + deletedDate: null, + reprompt: CipherRepromptType.None, + card: { + cardholderName: "EncryptedString", + brand: "EncryptedString", + number: "EncryptedString", + expMonth: "EncryptedString", + expYear: "EncryptedString", + code: "EncryptedString", + }, + }; + }); + + it("Convert", () => { + const cipher = new Cipher(cipherData); + + expect(cipher).toEqual({ + id: "id", + userId: "userId", + organizationId: "orgId", + folderId: "folderId", + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + notes: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 3, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: 0, + card: { + cardholderName: { encryptedString: "EncryptedString", encryptionType: 0 }, + brand: { encryptedString: "EncryptedString", encryptionType: 0 }, + number: { encryptedString: "EncryptedString", encryptionType: 0 }, + expMonth: { encryptedString: "EncryptedString", encryptionType: 0 }, + expYear: { encryptedString: "EncryptedString", encryptionType: 0 }, + code: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + attachments: null, + fields: null, + passwordHistory: null, + }); + }); + + it("toCipherData", () => { + const cipher = new Cipher(cipherData); + expect(cipher.toCipherData("userId")).toEqual(cipherData); + }); + + it("Decrypt", async () => { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.organizationId = "orgId"; + cipher.folderId = "folderId"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.organizationUseTotp = true; + cipher.favorite = false; + cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + cipher.type = CipherType.Card; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.deletedDate = null; + cipher.reprompt = CipherRepromptType.None; + + const cardView = new CardView(); + cardView.cardholderName = "cardholderName"; + cardView.number = "4111111111111111"; + + const card = Substitute.for(); + card.decrypt(Arg.any(), Arg.any()).resolves(cardView); + cipher.card = card; + + const cipherView = await cipher.decrypt(); + + expect(cipherView).toMatchObject({ + id: "id", + organizationId: "orgId", + folderId: "folderId", + name: "EncryptedString", + notes: "EncryptedString", + type: 3, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + card: cardView, + attachments: null, + fields: null, + passwordHistory: null, + collectionIds: undefined, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + deletedDate: null, + reprompt: 0, + localData: undefined, + }); + }); + }); + + describe("IdentityCipher", () => { + let cipherData: CipherData; + + beforeEach(() => { + cipherData = { + id: "id", + organizationId: "orgId", + folderId: "folderId", + userId: "userId", + edit: true, + viewPassword: true, + organizationUseTotp: true, + favorite: false, + revisionDate: "2022-01-31T12:00:00.000Z", + type: CipherType.Identity, + name: "EncryptedString", + notes: "EncryptedString", + deletedDate: null, + reprompt: CipherRepromptType.None, + identity: { + title: "EncryptedString", + firstName: "EncryptedString", + middleName: "EncryptedString", + lastName: "EncryptedString", + address1: "EncryptedString", + address2: "EncryptedString", + address3: "EncryptedString", + city: "EncryptedString", + state: "EncryptedString", + postalCode: "EncryptedString", + country: "EncryptedString", + company: "EncryptedString", + email: "EncryptedString", + phone: "EncryptedString", + ssn: "EncryptedString", + username: "EncryptedString", + passportNumber: "EncryptedString", + licenseNumber: "EncryptedString", + }, + }; + }); + + it("Convert", () => { + const cipher = new Cipher(cipherData); + + expect(cipher).toEqual({ + id: "id", + userId: "userId", + organizationId: "orgId", + folderId: "folderId", + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + notes: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 4, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: 0, + identity: { + title: { encryptedString: "EncryptedString", encryptionType: 0 }, + firstName: { encryptedString: "EncryptedString", encryptionType: 0 }, + middleName: { encryptedString: "EncryptedString", encryptionType: 0 }, + lastName: { encryptedString: "EncryptedString", encryptionType: 0 }, + address1: { encryptedString: "EncryptedString", encryptionType: 0 }, + address2: { encryptedString: "EncryptedString", encryptionType: 0 }, + address3: { encryptedString: "EncryptedString", encryptionType: 0 }, + city: { encryptedString: "EncryptedString", encryptionType: 0 }, + state: { encryptedString: "EncryptedString", encryptionType: 0 }, + postalCode: { encryptedString: "EncryptedString", encryptionType: 0 }, + country: { encryptedString: "EncryptedString", encryptionType: 0 }, + company: { encryptedString: "EncryptedString", encryptionType: 0 }, + email: { encryptedString: "EncryptedString", encryptionType: 0 }, + phone: { encryptedString: "EncryptedString", encryptionType: 0 }, + ssn: { encryptedString: "EncryptedString", encryptionType: 0 }, + username: { encryptedString: "EncryptedString", encryptionType: 0 }, + passportNumber: { encryptedString: "EncryptedString", encryptionType: 0 }, + licenseNumber: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + attachments: null, + fields: null, + passwordHistory: null, + }); + }); + + it("toCipherData", () => { + const cipher = new Cipher(cipherData); + expect(cipher.toCipherData("userId")).toEqual(cipherData); + }); + + it("Decrypt", async () => { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.organizationId = "orgId"; + cipher.folderId = "folderId"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.organizationUseTotp = true; + cipher.favorite = false; + cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + cipher.type = CipherType.Identity; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.deletedDate = null; + cipher.reprompt = CipherRepromptType.None; + + const identityView = new IdentityView(); + identityView.firstName = "firstName"; + identityView.lastName = "lastName"; + + const identity = Substitute.for(); + identity.decrypt(Arg.any(), Arg.any()).resolves(identityView); + cipher.identity = identity; + + const cipherView = await cipher.decrypt(); + + expect(cipherView).toMatchObject({ + id: "id", + organizationId: "orgId", + folderId: "folderId", + name: "EncryptedString", + notes: "EncryptedString", + type: 4, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + identity: identityView, + attachments: null, + fields: null, + passwordHistory: null, + collectionIds: undefined, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + deletedDate: null, + reprompt: 0, + localData: undefined, + }); + }); + }); +}); diff --git a/jslib/common/spec/domain/collection.spec.ts b/jslib/common/spec/domain/collection.spec.ts new file mode 100644 index 00000000..713bf2eb --- /dev/null +++ b/jslib/common/spec/domain/collection.spec.ts @@ -0,0 +1,66 @@ +import { CollectionData } from "jslib-common/models/data/collectionData"; +import { Collection } from "jslib-common/models/domain/collection"; + +import { mockEnc } from "../utils"; + +describe("Collection", () => { + let data: CollectionData; + + beforeEach(() => { + data = { + id: "id", + organizationId: "orgId", + name: "encName", + externalId: "extId", + readOnly: true, + }; + }); + + it("Convert from empty", () => { + const data = new CollectionData({} as any); + const card = new Collection(data); + + expect(card).toEqual({ + externalId: null, + hidePasswords: null, + id: null, + name: null, + organizationId: null, + readOnly: null, + }); + }); + + it("Convert", () => { + const collection = new Collection(data); + + expect(collection).toEqual({ + id: "id", + organizationId: "orgId", + name: { encryptedString: "encName", encryptionType: 0 }, + externalId: "extId", + readOnly: true, + hidePasswords: null, + }); + }); + + it("Decrypt", async () => { + const collection = new Collection(); + collection.id = "id"; + collection.organizationId = "orgId"; + collection.name = mockEnc("encName"); + collection.externalId = "extId"; + collection.readOnly = false; + collection.hidePasswords = false; + + const view = await collection.decrypt(); + + expect(view).toEqual({ + externalId: "extId", + hidePasswords: false, + id: "id", + name: "encName", + organizationId: "orgId", + readOnly: false, + }); + }); +}); diff --git a/jslib/common/spec/domain/encString.spec.ts b/jslib/common/spec/domain/encString.spec.ts new file mode 100644 index 00000000..e2477447 --- /dev/null +++ b/jslib/common/spec/domain/encString.spec.ts @@ -0,0 +1,195 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { EncryptionType } from "jslib-common/enums/encryptionType"; +import { EncString } from "jslib-common/models/domain/encString"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { ContainerService } from "jslib-common/services/container.service"; + +describe("EncString", () => { + afterEach(() => { + (window as any).bitwardenContainerService = undefined; + }); + + describe("Rsa2048_OaepSha256_B64", () => { + it("constructor", () => { + const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "3.data", + encryptionType: 3, + }); + }); + + describe("parse existing", () => { + it("valid", () => { + const encString = new EncString("3.data"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "3.data", + encryptionType: 3, + }); + }); + + it("invalid", () => { + const encString = new EncString("3.data|test"); + + expect(encString).toEqual({ + encryptedString: "3.data|test", + encryptionType: 3, + }); + }); + }); + + describe("decrypt", () => { + const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data"); + + const cryptoService = Substitute.for(); + cryptoService.getOrgKey(null).resolves(null); + cryptoService.decryptToUtf8(encString, Arg.any()).resolves("decrypted"); + + beforeEach(() => { + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + }); + + it("decrypts correctly", async () => { + const decrypted = await encString.decrypt(null); + + expect(decrypted).toBe("decrypted"); + }); + + it("result should be cached", async () => { + const decrypted = await encString.decrypt(null); + cryptoService.received(1).decryptToUtf8(Arg.any(), Arg.any()); + + expect(decrypted).toBe("decrypted"); + }); + }); + }); + + describe("AesCbc256_B64", () => { + it("constructor", () => { + const encString = new EncString(EncryptionType.AesCbc256_B64, "data", "iv"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "0.iv|data", + encryptionType: 0, + iv: "iv", + }); + }); + + describe("parse existing", () => { + it("valid", () => { + const encString = new EncString("0.iv|data"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "0.iv|data", + encryptionType: 0, + iv: "iv", + }); + }); + + it("invalid", () => { + const encString = new EncString("0.iv|data|mac"); + + expect(encString).toEqual({ + encryptedString: "0.iv|data|mac", + encryptionType: 0, + }); + }); + }); + }); + + describe("AesCbc256_HmacSha256_B64", () => { + it("constructor", () => { + const encString = new EncString(EncryptionType.AesCbc256_HmacSha256_B64, "data", "iv", "mac"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "2.iv|data|mac", + encryptionType: 2, + iv: "iv", + mac: "mac", + }); + }); + + it("valid", () => { + const encString = new EncString("2.iv|data|mac"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "2.iv|data|mac", + encryptionType: 2, + iv: "iv", + mac: "mac", + }); + }); + + it("invalid", () => { + const encString = new EncString("2.iv|data"); + + expect(encString).toEqual({ + encryptedString: "2.iv|data", + encryptionType: 2, + }); + }); + }); + + it("Exit early if null", () => { + const encString = new EncString(null); + + expect(encString).toEqual({ + encryptedString: null, + }); + }); + + describe("decrypt", () => { + it("throws exception when bitwarden container not initialized", async () => { + const encString = new EncString(null); + + expect.assertions(1); + try { + await encString.decrypt(null); + } catch (e) { + expect(e.message).toEqual("global bitwardenContainerService not initialized."); + } + }); + + it("handles value it can't decrypt", async () => { + const encString = new EncString(null); + + const cryptoService = Substitute.for(); + cryptoService.getOrgKey(null).resolves(null); + cryptoService.decryptToUtf8(encString, Arg.any()).throws("error"); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + + const decrypted = await encString.decrypt(null); + + expect(decrypted).toBe("[error: cannot decrypt]"); + + expect(encString).toEqual({ + decryptedValue: "[error: cannot decrypt]", + encryptedString: null, + }); + }); + + it("passes along key", async () => { + const encString = new EncString(null); + const key = Substitute.for(); + + const cryptoService = Substitute.for(); + cryptoService.getOrgKey(null).resolves(null); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + + await encString.decrypt(null, key); + + cryptoService.received().decryptToUtf8(encString, key); + }); + }); +}); diff --git a/jslib/common/spec/domain/field.spec.ts b/jslib/common/spec/domain/field.spec.ts new file mode 100644 index 00000000..40ceb710 --- /dev/null +++ b/jslib/common/spec/domain/field.spec.ts @@ -0,0 +1,64 @@ +import { FieldType } from "jslib-common/enums/fieldType"; +import { FieldData } from "jslib-common/models/data/fieldData"; +import { Field } from "jslib-common/models/domain/field"; + +import { mockEnc } from "../utils"; + +describe("Field", () => { + let data: FieldData; + + beforeEach(() => { + data = { + type: FieldType.Text, + name: "encName", + value: "encValue", + linkedId: null, + }; + }); + + it("Convert from empty", () => { + const data = new FieldData(); + const field = new Field(data); + + expect(field).toEqual({ + type: undefined, + name: null, + value: null, + linkedId: undefined, + }); + }); + + it("Convert", () => { + const field = new Field(data); + + expect(field).toEqual({ + type: FieldType.Text, + name: { encryptedString: "encName", encryptionType: 0 }, + value: { encryptedString: "encValue", encryptionType: 0 }, + linkedId: null, + }); + }); + + it("toFieldData", () => { + const field = new Field(data); + expect(field.toFieldData()).toEqual(data); + }); + + it("Decrypt", async () => { + const field = new Field(); + field.type = FieldType.Text; + field.name = mockEnc("encName"); + field.value = mockEnc("encValue"); + + const view = await field.decrypt(null); + + expect(view).toEqual({ + type: 0, + name: "encName", + value: "encValue", + newField: false, + showCount: false, + showValue: false, + }); + }); +}); diff --git a/jslib/common/spec/domain/folder.spec.ts b/jslib/common/spec/domain/folder.spec.ts new file mode 100644 index 00000000..64ee40b9 --- /dev/null +++ b/jslib/common/spec/domain/folder.spec.ts @@ -0,0 +1,42 @@ +import { FolderData } from "jslib-common/models/data/folderData"; +import { Folder } from "jslib-common/models/domain/folder"; + +import { mockEnc } from "../utils"; + +describe("Folder", () => { + let data: FolderData; + + beforeEach(() => { + data = { + id: "id", + userId: "userId", + name: "encName", + revisionDate: "2022-01-31T12:00:00.000Z", + }; + }); + + it("Convert", () => { + const field = new Folder(data); + + expect(field).toEqual({ + id: "id", + name: { encryptedString: "encName", encryptionType: 0 }, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + }); + }); + + it("Decrypt", async () => { + const folder = new Folder(); + folder.id = "id"; + folder.name = mockEnc("encName"); + folder.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + + const view = await folder.decrypt(); + + expect(view).toEqual({ + id: "id", + name: "encName", + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + }); + }); +}); diff --git a/jslib/common/spec/domain/identity.spec.ts b/jslib/common/spec/domain/identity.spec.ts new file mode 100644 index 00000000..ffaf5049 --- /dev/null +++ b/jslib/common/spec/domain/identity.spec.ts @@ -0,0 +1,134 @@ +import { IdentityData } from "jslib-common/models/data/identityData"; +import { Identity } from "jslib-common/models/domain/identity"; + +import { mockEnc } from "../utils"; + +describe("Identity", () => { + let data: IdentityData; + + beforeEach(() => { + data = { + title: "enctitle", + firstName: "encfirstName", + middleName: "encmiddleName", + lastName: "enclastName", + address1: "encaddress1", + address2: "encaddress2", + address3: "encaddress3", + city: "enccity", + state: "encstate", + postalCode: "encpostalCode", + country: "enccountry", + company: "enccompany", + email: "encemail", + phone: "encphone", + ssn: "encssn", + username: "encusername", + passportNumber: "encpassportNumber", + licenseNumber: "enclicenseNumber", + }; + }); + + it("Convert from empty", () => { + const data = new IdentityData(); + const identity = new Identity(data); + + expect(identity).toEqual({ + address1: null, + address2: null, + address3: null, + city: null, + company: null, + country: null, + email: null, + firstName: null, + lastName: null, + licenseNumber: null, + middleName: null, + passportNumber: null, + phone: null, + postalCode: null, + ssn: null, + state: null, + title: null, + username: null, + }); + }); + + it("Convert", () => { + const identity = new Identity(data); + + expect(identity).toEqual({ + title: { encryptedString: "enctitle", encryptionType: 0 }, + firstName: { encryptedString: "encfirstName", encryptionType: 0 }, + middleName: { encryptedString: "encmiddleName", encryptionType: 0 }, + lastName: { encryptedString: "enclastName", encryptionType: 0 }, + address1: { encryptedString: "encaddress1", encryptionType: 0 }, + address2: { encryptedString: "encaddress2", encryptionType: 0 }, + address3: { encryptedString: "encaddress3", encryptionType: 0 }, + city: { encryptedString: "enccity", encryptionType: 0 }, + state: { encryptedString: "encstate", encryptionType: 0 }, + postalCode: { encryptedString: "encpostalCode", encryptionType: 0 }, + country: { encryptedString: "enccountry", encryptionType: 0 }, + company: { encryptedString: "enccompany", encryptionType: 0 }, + email: { encryptedString: "encemail", encryptionType: 0 }, + phone: { encryptedString: "encphone", encryptionType: 0 }, + ssn: { encryptedString: "encssn", encryptionType: 0 }, + username: { encryptedString: "encusername", encryptionType: 0 }, + passportNumber: { encryptedString: "encpassportNumber", encryptionType: 0 }, + licenseNumber: { encryptedString: "enclicenseNumber", encryptionType: 0 }, + }); + }); + + it("toIdentityData", () => { + const identity = new Identity(data); + expect(identity.toIdentityData()).toEqual(data); + }); + + it("Decrypt", async () => { + const identity = new Identity(); + + identity.title = mockEnc("mockTitle"); + identity.firstName = mockEnc("mockFirstName"); + identity.middleName = mockEnc("mockMiddleName"); + identity.lastName = mockEnc("mockLastName"); + identity.address1 = mockEnc("mockAddress1"); + identity.address2 = mockEnc("mockAddress2"); + identity.address3 = mockEnc("mockAddress3"); + identity.city = mockEnc("mockCity"); + identity.state = mockEnc("mockState"); + identity.postalCode = mockEnc("mockPostalCode"); + identity.country = mockEnc("mockCountry"); + identity.company = mockEnc("mockCompany"); + identity.email = mockEnc("mockEmail"); + identity.phone = mockEnc("mockPhone"); + identity.ssn = mockEnc("mockSsn"); + identity.username = mockEnc("mockUsername"); + identity.passportNumber = mockEnc("mockPassportNumber"); + identity.licenseNumber = mockEnc("mockLicenseNumber"); + + const view = await identity.decrypt(null); + + expect(view).toEqual({ + _firstName: "mockFirstName", + _lastName: "mockLastName", + _subTitle: null, + address1: "mockAddress1", + address2: "mockAddress2", + address3: "mockAddress3", + city: "mockCity", + company: "mockCompany", + country: "mockCountry", + email: "mockEmail", + licenseNumber: "mockLicenseNumber", + middleName: "mockMiddleName", + passportNumber: "mockPassportNumber", + phone: "mockPhone", + postalCode: "mockPostalCode", + ssn: "mockSsn", + state: "mockState", + title: "mockTitle", + username: "mockUsername", + }); + }); +}); diff --git a/jslib/common/spec/domain/login.spec.ts b/jslib/common/spec/domain/login.spec.ts new file mode 100644 index 00000000..88c31a5f --- /dev/null +++ b/jslib/common/spec/domain/login.spec.ts @@ -0,0 +1,101 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { UriMatchType } from "jslib-common/enums/uriMatchType"; +import { LoginData } from "jslib-common/models/data/loginData"; +import { Login } from "jslib-common/models/domain/login"; +import { LoginUri } from "jslib-common/models/domain/loginUri"; +import { LoginUriView } from "jslib-common/models/view/loginUriView"; + +import { mockEnc } from "../utils"; + +describe("Login DTO", () => { + it("Convert from empty LoginData", () => { + const data = new LoginData(); + const login = new Login(data); + + expect(login).toEqual({ + passwordRevisionDate: null, + autofillOnPageLoad: undefined, + username: null, + password: null, + totp: null, + }); + }); + + it("Convert from full LoginData", () => { + const data: LoginData = { + uris: [{ uri: "uri", match: UriMatchType.Domain }], + username: "username", + password: "password", + passwordRevisionDate: "2022-01-31T12:00:00.000Z", + totp: "123", + autofillOnPageLoad: false, + }; + const login = new Login(data); + + expect(login).toEqual({ + passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"), + autofillOnPageLoad: false, + username: { encryptedString: "username", encryptionType: 0 }, + password: { encryptedString: "password", encryptionType: 0 }, + totp: { encryptedString: "123", encryptionType: 0 }, + uris: [{ match: 0, uri: { encryptedString: "uri", encryptionType: 0 } }], + }); + }); + + it("Initialize without LoginData", () => { + const login = new Login(); + + expect(login).toEqual({}); + }); + + it("Decrypts correctly", async () => { + const loginUri = Substitute.for(); + const loginUriView = new LoginUriView(); + loginUriView.uri = "decrypted uri"; + loginUri.decrypt(Arg.any()).resolves(loginUriView); + + const login = new Login(); + login.uris = [loginUri]; + login.username = mockEnc("encrypted username"); + login.password = mockEnc("encrypted password"); + login.passwordRevisionDate = new Date("2022-01-31T12:00:00.000Z"); + login.totp = mockEnc("encrypted totp"); + login.autofillOnPageLoad = true; + + const loginView = await login.decrypt(null); + expect(loginView).toEqual({ + username: "encrypted username", + password: "encrypted password", + passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"), + totp: "encrypted totp", + uris: [ + { + match: null, + _uri: "decrypted uri", + _domain: null, + _hostname: null, + _host: null, + _canLaunch: null, + }, + ], + autofillOnPageLoad: true, + }); + }); + + it("Converts from LoginData and back", () => { + const data: LoginData = { + uris: [{ uri: "uri", match: UriMatchType.Domain }], + username: "username", + password: "password", + passwordRevisionDate: "2022-01-31T12:00:00.000Z", + totp: "123", + autofillOnPageLoad: false, + }; + const login = new Login(data); + + const loginData = login.toLoginData(); + + expect(loginData).toEqual(data); + }); +}); diff --git a/jslib/common/spec/domain/loginUri.spec.ts b/jslib/common/spec/domain/loginUri.spec.ts new file mode 100644 index 00000000..2ea5ab59 --- /dev/null +++ b/jslib/common/spec/domain/loginUri.spec.ts @@ -0,0 +1,57 @@ +import { UriMatchType } from "jslib-common/enums/uriMatchType"; +import { LoginUriData } from "jslib-common/models/data/loginUriData"; +import { LoginUri } from "jslib-common/models/domain/loginUri"; + +import { mockEnc } from "../utils"; + +describe("LoginUri", () => { + let data: LoginUriData; + + beforeEach(() => { + data = { + uri: "encUri", + match: UriMatchType.Domain, + }; + }); + + it("Convert from empty", () => { + const data = new LoginUriData(); + const loginUri = new LoginUri(data); + + expect(loginUri).toEqual({ + match: null, + uri: null, + }); + }); + + it("Convert", () => { + const loginUri = new LoginUri(data); + + expect(loginUri).toEqual({ + match: 0, + uri: { encryptedString: "encUri", encryptionType: 0 }, + }); + }); + + it("toLoginUriData", () => { + const loginUri = new LoginUri(data); + expect(loginUri.toLoginUriData()).toEqual(data); + }); + + it("Decrypt", async () => { + const loginUri = new LoginUri(); + loginUri.match = UriMatchType.Exact; + loginUri.uri = mockEnc("uri"); + + const view = await loginUri.decrypt(null); + + expect(view).toEqual({ + _canLaunch: null, + _domain: null, + _host: null, + _hostname: null, + _uri: "uri", + match: 3, + }); + }); +}); diff --git a/jslib/common/spec/domain/password.spec.ts b/jslib/common/spec/domain/password.spec.ts new file mode 100644 index 00000000..4054c951 --- /dev/null +++ b/jslib/common/spec/domain/password.spec.ts @@ -0,0 +1,51 @@ +import { PasswordHistoryData } from "jslib-common/models/data/passwordHistoryData"; +import { Password } from "jslib-common/models/domain/password"; + +import { mockEnc } from "../utils"; + +describe("Password", () => { + let data: PasswordHistoryData; + + beforeEach(() => { + data = { + password: "encPassword", + lastUsedDate: "2022-01-31T12:00:00.000Z", + }; + }); + + it("Convert from empty", () => { + const data = new PasswordHistoryData(); + const password = new Password(data); + + expect(password).toMatchObject({ + password: null, + }); + }); + + it("Convert", () => { + const password = new Password(data); + + expect(password).toEqual({ + password: { encryptedString: "encPassword", encryptionType: 0 }, + lastUsedDate: new Date("2022-01-31T12:00:00.000Z"), + }); + }); + + it("toPasswordHistoryData", () => { + const password = new Password(data); + expect(password.toPasswordHistoryData()).toEqual(data); + }); + + it("Decrypt", async () => { + const password = new Password(); + password.password = mockEnc("password"); + password.lastUsedDate = new Date("2022-01-31T12:00:00.000Z"); + + const view = await password.decrypt(null); + + expect(view).toEqual({ + password: "password", + lastUsedDate: new Date("2022-01-31T12:00:00.000Z"), + }); + }); +}); diff --git a/jslib/common/spec/domain/secureNote.spec.ts b/jslib/common/spec/domain/secureNote.spec.ts new file mode 100644 index 00000000..50ca67ed --- /dev/null +++ b/jslib/common/spec/domain/secureNote.spec.ts @@ -0,0 +1,46 @@ +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { SecureNoteData } from "jslib-common/models/data/secureNoteData"; +import { SecureNote } from "jslib-common/models/domain/secureNote"; + +describe("SecureNote", () => { + let data: SecureNoteData; + + beforeEach(() => { + data = { + type: SecureNoteType.Generic, + }; + }); + + it("Convert from empty", () => { + const data = new SecureNoteData(); + const secureNote = new SecureNote(data); + + expect(secureNote).toEqual({ + type: undefined, + }); + }); + + it("Convert", () => { + const secureNote = new SecureNote(data); + + expect(secureNote).toEqual({ + type: 0, + }); + }); + + it("toSecureNoteData", () => { + const secureNote = new SecureNote(data); + expect(secureNote.toSecureNoteData()).toEqual(data); + }); + + it("Decrypt", async () => { + const secureNote = new SecureNote(); + secureNote.type = SecureNoteType.Generic; + + const view = await secureNote.decrypt(null); + + expect(view).toEqual({ + type: 0, + }); + }); +}); diff --git a/jslib/common/spec/domain/send.spec.ts b/jslib/common/spec/domain/send.spec.ts new file mode 100644 index 00000000..d63c622a --- /dev/null +++ b/jslib/common/spec/domain/send.spec.ts @@ -0,0 +1,144 @@ +import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { SendType } from "jslib-common/enums/sendType"; +import { SendData } from "jslib-common/models/data/sendData"; +import { EncString } from "jslib-common/models/domain/encString"; +import { Send } from "jslib-common/models/domain/send"; +import { SendText } from "jslib-common/models/domain/sendText"; +import { ContainerService } from "jslib-common/services/container.service"; + +import { makeStaticByteArray, mockEnc } from "../utils"; + +describe("Send", () => { + let data: SendData; + + beforeEach(() => { + data = { + id: "id", + accessId: "accessId", + userId: "userId", + type: SendType.Text, + name: "encName", + notes: "encNotes", + text: { + text: "encText", + hidden: true, + }, + file: null, + key: "encKey", + maxAccessCount: null, + accessCount: 10, + revisionDate: "2022-01-31T12:00:00.000Z", + expirationDate: "2022-01-31T12:00:00.000Z", + deletionDate: "2022-01-31T12:00:00.000Z", + password: "password", + disabled: false, + hideEmail: true, + }; + }); + + it("Convert from empty", () => { + const data = new SendData(); + const send = new Send(data); + + expect(send).toEqual({ + id: null, + accessId: null, + userId: null, + type: undefined, + name: null, + notes: null, + text: undefined, + file: undefined, + key: null, + maxAccessCount: undefined, + accessCount: undefined, + revisionDate: null, + expirationDate: null, + deletionDate: null, + password: undefined, + disabled: undefined, + hideEmail: undefined, + }); + }); + + it("Convert", () => { + const send = new Send(data); + + expect(send).toEqual({ + id: "id", + accessId: "accessId", + userId: "userId", + type: SendType.Text, + name: { encryptedString: "encName", encryptionType: 0 }, + notes: { encryptedString: "encNotes", encryptionType: 0 }, + text: { + text: { encryptedString: "encText", encryptionType: 0 }, + hidden: true, + }, + key: { encryptedString: "encKey", encryptionType: 0 }, + maxAccessCount: null, + accessCount: 10, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + deletionDate: new Date("2022-01-31T12:00:00.000Z"), + password: "password", + disabled: false, + hideEmail: true, + }); + }); + + it("Decrypt", async () => { + const text = Substitute.for(); + text.decrypt(Arg.any()).resolves("textView" as any); + + const send = new Send(); + send.id = "id"; + send.accessId = "accessId"; + send.userId = "userId"; + send.type = SendType.Text; + send.name = mockEnc("name"); + send.notes = mockEnc("notes"); + send.text = text; + send.key = mockEnc("key"); + send.accessCount = 10; + send.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + send.expirationDate = new Date("2022-01-31T12:00:00.000Z"); + send.deletionDate = new Date("2022-01-31T12:00:00.000Z"); + send.password = "password"; + send.disabled = false; + send.hideEmail = true; + + const cryptoService = Substitute.for(); + cryptoService.decryptToBytes(send.key, null).resolves(makeStaticByteArray(32)); + cryptoService.makeSendKey(Arg.any()).resolves("cryptoKey" as any); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + + const view = await send.decrypt(); + + text.received(1).decrypt("cryptoKey" as any); + (send.name as SubstituteOf).received(1).decrypt(null, "cryptoKey" as any); + + expect(view).toMatchObject({ + id: "id", + accessId: "accessId", + name: "name", + notes: "notes", + type: 0, + key: expect.anything(), + cryptoKey: "cryptoKey", + file: expect.anything(), + text: "textView", + maxAccessCount: undefined, + accessCount: 10, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + deletionDate: new Date("2022-01-31T12:00:00.000Z"), + password: "password", + disabled: false, + hideEmail: true, + }); + }); +}); diff --git a/jslib/common/spec/domain/sendAccess.spec.ts b/jslib/common/spec/domain/sendAccess.spec.ts new file mode 100644 index 00000000..fc3001a5 --- /dev/null +++ b/jslib/common/spec/domain/sendAccess.spec.ts @@ -0,0 +1,84 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { SendType } from "jslib-common/enums/sendType"; +import { SendAccess } from "jslib-common/models/domain/sendAccess"; +import { SendText } from "jslib-common/models/domain/sendText"; +import { SendAccessResponse } from "jslib-common/models/response/sendAccessResponse"; + +import { mockEnc } from "../utils"; + +describe("SendAccess", () => { + let request: SendAccessResponse; + + beforeEach(() => { + request = { + id: "id", + type: SendType.Text, + name: "encName", + file: null, + text: { + text: "encText", + hidden: true, + }, + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + creatorIdentifier: "creatorIdentifier", + } as SendAccessResponse; + }); + + it("Convert from empty", () => { + const request = new SendAccessResponse({}); + const sendAccess = new SendAccess(request); + + expect(sendAccess).toEqual({ + id: null, + type: undefined, + name: null, + creatorIdentifier: null, + expirationDate: null, + }); + }); + + it("Convert", () => { + const sendAccess = new SendAccess(request); + + expect(sendAccess).toEqual({ + id: "id", + type: 0, + name: { encryptedString: "encName", encryptionType: 0 }, + text: { + hidden: true, + text: { encryptedString: "encText", encryptionType: 0 }, + }, + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + creatorIdentifier: "creatorIdentifier", + }); + }); + + it("Decrypt", async () => { + const sendAccess = new SendAccess(); + sendAccess.id = "id"; + sendAccess.type = SendType.Text; + sendAccess.name = mockEnc("name"); + + const text = Substitute.for(); + text.decrypt(Arg.any()).resolves({} as any); + sendAccess.text = text; + + sendAccess.expirationDate = new Date("2022-01-31T12:00:00.000Z"); + sendAccess.creatorIdentifier = "creatorIdentifier"; + + const view = await sendAccess.decrypt(null); + + text.received(1).decrypt(Arg.any()); + + expect(view).toEqual({ + id: "id", + type: 0, + name: "name", + text: {}, + file: expect.anything(), + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + creatorIdentifier: "creatorIdentifier", + }); + }); +}); diff --git a/jslib/common/spec/domain/sendFile.spec.ts b/jslib/common/spec/domain/sendFile.spec.ts new file mode 100644 index 00000000..9fde0b76 --- /dev/null +++ b/jslib/common/spec/domain/sendFile.spec.ts @@ -0,0 +1,57 @@ +import { SendFileData } from "jslib-common/models/data/sendFileData"; +import { SendFile } from "jslib-common/models/domain/sendFile"; + +import { mockEnc } from "../utils"; + +describe("SendFile", () => { + let data: SendFileData; + + beforeEach(() => { + data = { + id: "id", + size: "1100", + sizeName: "1.1 KB", + fileName: "encFileName", + }; + }); + + it("Convert from empty", () => { + const data = new SendFileData(); + const sendFile = new SendFile(data); + + expect(sendFile).toEqual({ + fileName: null, + id: null, + size: undefined, + sizeName: null, + }); + }); + + it("Convert", () => { + const sendFile = new SendFile(data); + + expect(sendFile).toEqual({ + id: "id", + size: "1100", + sizeName: "1.1 KB", + fileName: { encryptedString: "encFileName", encryptionType: 0 }, + }); + }); + + it("Decrypt", async () => { + const sendFile = new SendFile(); + sendFile.id = "id"; + sendFile.size = "1100"; + sendFile.sizeName = "1.1 KB"; + sendFile.fileName = mockEnc("fileName"); + + const view = await sendFile.decrypt(null); + + expect(view).toEqual({ + fileName: "fileName", + id: "id", + size: "1100", + sizeName: "1.1 KB", + }); + }); +}); diff --git a/jslib/common/spec/domain/sendText.spec.ts b/jslib/common/spec/domain/sendText.spec.ts new file mode 100644 index 00000000..776a4c57 --- /dev/null +++ b/jslib/common/spec/domain/sendText.spec.ts @@ -0,0 +1,47 @@ +import { SendTextData } from "jslib-common/models/data/sendTextData"; +import { SendText } from "jslib-common/models/domain/sendText"; + +import { mockEnc } from "../utils"; + +describe("SendText", () => { + let data: SendTextData; + + beforeEach(() => { + data = { + text: "encText", + hidden: false, + }; + }); + + it("Convert from empty", () => { + const data = new SendTextData(); + const secureNote = new SendText(data); + + expect(secureNote).toEqual({ + hidden: undefined, + text: null, + }); + }); + + it("Convert", () => { + const secureNote = new SendText(data); + + expect(secureNote).toEqual({ + hidden: false, + text: { encryptedString: "encText", encryptionType: 0 }, + }); + }); + + it("Decrypt", async () => { + const secureNote = new SendText(); + secureNote.text = mockEnc("text"); + secureNote.hidden = true; + + const view = await secureNote.decrypt(null); + + expect(view).toEqual({ + text: "text", + hidden: true, + }); + }); +}); diff --git a/jslib/common/spec/domain/symmetricCryptoKey.spec.ts b/jslib/common/spec/domain/symmetricCryptoKey.spec.ts new file mode 100644 index 00000000..62f096c1 --- /dev/null +++ b/jslib/common/spec/domain/symmetricCryptoKey.spec.ts @@ -0,0 +1,69 @@ +import { EncryptionType } from "jslib-common/enums/encryptionType"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; + +import { makeStaticByteArray } from "../utils"; + +describe("SymmetricCryptoKey", () => { + it("errors if no key", () => { + const t = () => { + new SymmetricCryptoKey(null); + }; + + expect(t).toThrowError("Must provide key"); + }); + + describe("guesses encKey from key length", () => { + it("AesCbc256_B64", () => { + const key = makeStaticByteArray(32); + const cryptoKey = new SymmetricCryptoKey(key); + + expect(cryptoKey).toEqual({ + encKey: key, + encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", + encType: 0, + key: key, + keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", + macKey: null, + }); + }); + + it("AesCbc128_HmacSha256_B64", () => { + const key = makeStaticByteArray(32); + const cryptoKey = new SymmetricCryptoKey(key, EncryptionType.AesCbc128_HmacSha256_B64); + + expect(cryptoKey).toEqual({ + encKey: key.slice(0, 16), + encKeyB64: "AAECAwQFBgcICQoLDA0ODw==", + encType: 1, + key: key, + keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", + macKey: key.slice(16, 32), + macKeyB64: "EBESExQVFhcYGRobHB0eHw==", + }); + }); + + it("AesCbc256_HmacSha256_B64", () => { + const key = makeStaticByteArray(64); + const cryptoKey = new SymmetricCryptoKey(key); + + expect(cryptoKey).toEqual({ + encKey: key.slice(0, 32), + encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", + encType: 2, + key: key, + keyB64: + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==", + macKey: key.slice(32, 64), + macKeyB64: "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=", + }); + }); + + it("unknown length", () => { + const t = () => { + new SymmetricCryptoKey(makeStaticByteArray(30)); + }; + + expect(t).toThrowError("Unable to determine encType."); + }); + }); +}); diff --git a/jslib/common/spec/importers/bitwardenJsonImporter.spec.ts b/jslib/common/spec/importers/bitwardenJsonImporter.spec.ts new file mode 100644 index 00000000..94b03528 --- /dev/null +++ b/jslib/common/spec/importers/bitwardenJsonImporter.spec.ts @@ -0,0 +1,31 @@ +import { Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { BitwardenJsonImporter } from "jslib-common/importers/bitwardenJsonImporter"; + +import { data as passwordProtectedData } from "./testData/bitwardenJson/passwordProtected.json"; + +describe("bitwarden json importer", () => { + let sut: BitwardenJsonImporter; + let cryptoService: SubstituteOf; + let i18nService: SubstituteOf; + + beforeEach(() => { + cryptoService = Substitute.for(); + i18nService = Substitute.for(); + + sut = new BitwardenJsonImporter(cryptoService, i18nService); + }); + + it("should fail if password is needed", async () => { + expect((await sut.parse(passwordProtectedData)).success).toBe(false); + }); + + it("should return password needed error message", async () => { + const expected = "Password required error message"; + i18nService.t("importPasswordRequired").returns(expected); + + expect((await sut.parse(passwordProtectedData)).errorMessage).toEqual(expected); + }); +}); diff --git a/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts b/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts new file mode 100644 index 00000000..ed2434ee --- /dev/null +++ b/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts @@ -0,0 +1,113 @@ +import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { KdfType } from "jslib-common/enums/kdfType"; +import { BitwardenPasswordProtectedImporter } from "jslib-common/importers/bitwardenPasswordProtectedImporter"; +import { Utils } from "jslib-common/misc/utils"; +import { ImportResult } from "jslib-common/models/domain/importResult"; + +import { data as emptyDecryptedData } from "./testData/bitwardenJson/empty.json"; + +describe("BitwardenPasswordProtectedImporter", () => { + let importer: BitwardenPasswordProtectedImporter; + let cryptoService: SubstituteOf; + let i18nService: SubstituteOf; + const password = Utils.newGuid(); + const result = new ImportResult(); + let jDoc: { + encrypted?: boolean; + passwordProtected?: boolean; + salt?: string; + kdfIterations?: any; + kdfType?: any; + encKeyValidation_DO_NOT_EDIT?: string; + data?: string; + }; + + beforeEach(() => { + cryptoService = Substitute.for(); + i18nService = Substitute.for(); + + jDoc = { + encrypted: true, + passwordProtected: true, + salt: "c2FsdA==", + kdfIterations: 100000, + kdfType: KdfType.PBKDF2_SHA256, + encKeyValidation_DO_NOT_EDIT: Utils.newGuid(), + data: Utils.newGuid(), + }; + + result.success = true; + importer = new BitwardenPasswordProtectedImporter(cryptoService, i18nService, password); + }); + + describe("Required Json Data", () => { + it("succeeds with default jdoc", async () => { + cryptoService.decryptToUtf8(Arg.any(), Arg.any()).resolves(emptyDecryptedData); + + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(true); + }); + + it("fails if encrypted === false", async () => { + jDoc.encrypted = false; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if encrypted === null", async () => { + jDoc.encrypted = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if passwordProtected === false", async () => { + jDoc.passwordProtected = false; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if passwordProtected === null", async () => { + jDoc.passwordProtected = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if salt === null", async () => { + jDoc.salt = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfIterations === null", async () => { + jDoc.kdfIterations = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfIterations is not a number", async () => { + jDoc.kdfIterations = "not a number"; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfType === null", async () => { + jDoc.kdfType = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfType is not a string", async () => { + jDoc.kdfType = "not a valid kdf type"; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfType is not a known kdfType", async () => { + jDoc.kdfType = -1; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if encKeyValidation_DO_NOT_EDIT === null", async () => { + jDoc.encKeyValidation_DO_NOT_EDIT = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if data === null", async () => { + jDoc.data = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + }); +}); diff --git a/jslib/common/spec/importers/dashlaneCsvImporter.spec.ts b/jslib/common/spec/importers/dashlaneCsvImporter.spec.ts new file mode 100644 index 00000000..666e3232 --- /dev/null +++ b/jslib/common/spec/importers/dashlaneCsvImporter.spec.ts @@ -0,0 +1,367 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { DashlaneCsvImporter as Importer } from "jslib-common/importers/dashlaneImporters/dashlaneCsvImporter"; + +import { credentialsData } from "./testData/dashlaneCsv/credentials.csv"; +import { identityData } from "./testData/dashlaneCsv/id.csv"; +import { multiplePersonalInfoData } from "./testData/dashlaneCsv/multiplePersonalInfo.csv"; +import { paymentsData } from "./testData/dashlaneCsv/payments.csv"; +import { personalInfoData } from "./testData/dashlaneCsv/personalInfo.csv"; +import { secureNoteData } from "./testData/dashlaneCsv/securenotes.csv"; + +describe("Dashlane CSV Importer", () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse login records", async () => { + const result = await importer.parse(credentialsData); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("example.com"); + expect(cipher.login.username).toEqual("jdoe"); + expect(cipher.login.password).toEqual("somePassword"); + expect(cipher.login.totp).toEqual("someTOTPSeed"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://www.example.com"); + expect(cipher.notes).toEqual("some note for example.com"); + }); + + it("should parse an item and create a folder", async () => { + const result = await importer.parse(credentialsData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.folders.length).toBe(1); + expect(result.folders[0].name).toBe("Entertainment"); + expect(result.folderRelationships[0]).toEqual([0, 0]); + }); + + it("should parse payment records", async () => { + const result = await importer.parse(paymentsData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(2); + + // Account + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Card); + expect(cipher.name).toBe("John's savings account"); + expect(cipher.card.brand).toBeNull(); + expect(cipher.card.cardholderName).toBe("John Doe"); + expect(cipher.card.number).toBe("accountNumber"); + expect(cipher.card.code).toBeNull(); + expect(cipher.card.expMonth).toBeNull(); + expect(cipher.card.expYear).toBeNull(); + + expect(cipher.fields.length).toBe(4); + + expect(cipher.fields[0].name).toBe("type"); + expect(cipher.fields[0].value).toBe("bank"); + + expect(cipher.fields[1].name).toBe("routing_number"); + expect(cipher.fields[1].value).toBe("routingNumber"); + + expect(cipher.fields[2].name).toBe("country"); + expect(cipher.fields[2].value).toBe("US"); + + expect(cipher.fields[3].name).toBe("issuing_bank"); + expect(cipher.fields[3].value).toBe("US-ALLY"); + + // CreditCard + const cipher2 = result.ciphers.shift(); + expect(cipher2.type).toBe(CipherType.Card); + expect(cipher2.name).toBe("John Doe"); + expect(cipher2.card.brand).toBe("Visa"); + expect(cipher2.card.cardholderName).toBe("John Doe"); + expect(cipher2.card.number).toBe("41111111111111111"); + expect(cipher2.card.code).toBe("123"); + expect(cipher2.card.expMonth).toBe("01"); + expect(cipher2.card.expYear).toBe("23"); + + expect(cipher2.fields.length).toBe(2); + + expect(cipher2.fields[0].name).toBe("type"); + expect(cipher2.fields[0].value).toBe("credit_card"); + + expect(cipher2.fields[1].name).toBe("country"); + expect(cipher2.fields[1].value).toBe("US"); + }); + + it("should parse ids records", async () => { + const result = await importer.parse(identityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + + // Type card + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("John Doe card"); + expect(cipher.identity.fullName).toBe("John Doe"); + expect(cipher.identity.firstName).toBe("John"); + expect(cipher.identity.middleName).toBeNull(); + expect(cipher.identity.lastName).toBe("Doe"); + expect(cipher.identity.licenseNumber).toBe("123123123"); + + expect(cipher.fields.length).toBe(3); + + expect(cipher.fields[0].name).toEqual("type"); + expect(cipher.fields[0].value).toEqual("card"); + + expect(cipher.fields[1].name).toEqual("issue_date"); + expect(cipher.fields[1].value).toEqual("2022-1-30"); + + expect(cipher.fields[2].name).toEqual("expiration_date"); + expect(cipher.fields[2].value).toEqual("2032-1-30"); + + // Type passport + const cipher2 = result.ciphers.shift(); + expect(cipher2.type).toBe(CipherType.Identity); + expect(cipher2.name).toBe("John Doe passport"); + expect(cipher2.identity.fullName).toBe("John Doe"); + expect(cipher2.identity.firstName).toBe("John"); + expect(cipher2.identity.middleName).toBeNull(); + expect(cipher2.identity.lastName).toBe("Doe"); + expect(cipher2.identity.passportNumber).toBe("123123123"); + + expect(cipher2.fields.length).toBe(4); + + expect(cipher2.fields[0].name).toEqual("type"); + expect(cipher2.fields[0].value).toEqual("passport"); + expect(cipher2.fields[1].name).toEqual("issue_date"); + expect(cipher2.fields[1].value).toEqual("2022-1-30"); + expect(cipher2.fields[2].name).toEqual("expiration_date"); + expect(cipher2.fields[2].value).toEqual("2032-1-30"); + expect(cipher2.fields[3].name).toEqual("place_of_issue"); + expect(cipher2.fields[3].value).toEqual("somewhere in Germany"); + + // Type license + const cipher3 = result.ciphers.shift(); + expect(cipher3.type).toBe(CipherType.Identity); + expect(cipher3.name).toBe("John Doe license"); + expect(cipher3.identity.fullName).toBe("John Doe"); + expect(cipher3.identity.firstName).toBe("John"); + expect(cipher3.identity.middleName).toBeNull(); + expect(cipher3.identity.lastName).toBe("Doe"); + expect(cipher3.identity.licenseNumber).toBe("1234556"); + expect(cipher3.identity.state).toBe("DC"); + + expect(cipher3.fields.length).toBe(3); + expect(cipher3.fields[0].name).toEqual("type"); + expect(cipher3.fields[0].value).toEqual("license"); + expect(cipher3.fields[1].name).toEqual("issue_date"); + expect(cipher3.fields[1].value).toEqual("2022-8-10"); + expect(cipher3.fields[2].name).toEqual("expiration_date"); + expect(cipher3.fields[2].value).toEqual("2022-10-10"); + + // Type social_security + const cipher4 = result.ciphers.shift(); + expect(cipher4.type).toBe(CipherType.Identity); + expect(cipher4.name).toBe("John Doe social_security"); + expect(cipher4.identity.fullName).toBe("John Doe"); + expect(cipher4.identity.firstName).toBe("John"); + expect(cipher4.identity.middleName).toBeNull(); + expect(cipher4.identity.lastName).toBe("Doe"); + expect(cipher4.identity.ssn).toBe("123123123"); + + expect(cipher4.fields.length).toBe(1); + expect(cipher4.fields[0].name).toEqual("type"); + expect(cipher4.fields[0].value).toEqual("social_security"); + + // Type tax_number + const cipher5 = result.ciphers.shift(); + expect(cipher5.type).toBe(CipherType.Identity); + expect(cipher5.name).toBe("tax_number"); + expect(cipher5.identity.licenseNumber).toBe("123123123"); + + expect(cipher5.fields.length).toBe(1); + expect(cipher5.fields[0].name).toEqual("type"); + expect(cipher5.fields[0].value).toEqual("tax_number"); + }); + + it("should parse secureNote records", async () => { + const result = await importer.parse(secureNoteData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.SecureNote); + expect(cipher.name).toBe("01"); + expect(cipher.notes).toBe("test"); + }); + + it("should parse personal information records (multiple identities)", async () => { + const result = await importer.parse(multiplePersonalInfoData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(6); + + // name + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.SecureNote); + expect(cipher.name).toBe("MR John Doe"); + + expect(cipher.fields.length).toBe(7); + expect(cipher.fields[0].name).toEqual("type"); + expect(cipher.fields[0].value).toEqual("name"); + expect(cipher.fields[1].name).toEqual("title"); + expect(cipher.fields[1].value).toEqual("MR"); + expect(cipher.fields[2].name).toEqual("first_name"); + expect(cipher.fields[2].value).toEqual("John"); + expect(cipher.fields[3].name).toEqual("last_name"); + expect(cipher.fields[3].value).toEqual("Doe"); + expect(cipher.fields[4].name).toEqual("login"); + expect(cipher.fields[4].value).toEqual("jdoe"); + expect(cipher.fields[5].name).toEqual("date_of_birth"); + expect(cipher.fields[5].value).toEqual("2022-01-30"); + expect(cipher.fields[6].name).toEqual("place_of_birth"); + expect(cipher.fields[6].value).toEqual("world"); + + // email + const cipher2 = result.ciphers.shift(); + expect(cipher2.type).toBe(CipherType.SecureNote); + expect(cipher2.name).toBe("Johns email"); + + expect(cipher2.fields.length).toBe(4); + expect(cipher2.fields[0].name).toEqual("type"); + expect(cipher2.fields[0].value).toEqual("email"); + expect(cipher2.fields[1].name).toEqual("email"); + expect(cipher2.fields[1].value).toEqual("jdoe@example.com"); + expect(cipher2.fields[2].name).toEqual("email_type"); + expect(cipher2.fields[2].value).toEqual("personal"); + expect(cipher2.fields[3].name).toEqual("item_name"); + expect(cipher2.fields[3].value).toEqual("Johns email"); + + // number + const cipher3 = result.ciphers.shift(); + expect(cipher3.type).toBe(CipherType.SecureNote); + expect(cipher3.name).toBe("John's number"); + + expect(cipher3.fields.length).toBe(3); + expect(cipher3.fields[0].name).toEqual("type"); + expect(cipher3.fields[0].value).toEqual("number"); + expect(cipher3.fields[1].name).toEqual("item_name"); + expect(cipher3.fields[1].value).toEqual("John's number"); + expect(cipher3.fields[2].name).toEqual("phone_number"); + expect(cipher3.fields[2].value).toEqual("+49123123123"); + + // address + const cipher4 = result.ciphers.shift(); + expect(cipher4.type).toBe(CipherType.SecureNote); + expect(cipher4.name).toBe("John's home address"); + + expect(cipher4.fields.length).toBe(12); + expect(cipher4.fields[0].name).toEqual("type"); + expect(cipher4.fields[0].value).toEqual("address"); + expect(cipher4.fields[1].name).toEqual("item_name"); + expect(cipher4.fields[1].value).toEqual("John's home address"); + expect(cipher4.fields[2].name).toEqual("address"); + expect(cipher4.fields[2].value).toEqual("1 some street"); + expect(cipher4.fields[3].name).toEqual("country"); + expect(cipher4.fields[3].value).toEqual("de"); + expect(cipher4.fields[4].name).toEqual("state"); + expect(cipher4.fields[4].value).toEqual("DE-0-NW"); + expect(cipher4.fields[5].name).toEqual("city"); + expect(cipher4.fields[5].value).toEqual("some city"); + expect(cipher4.fields[6].name).toEqual("zip"); + expect(cipher4.fields[6].value).toEqual("123123"); + expect(cipher4.fields[7].name).toEqual("address_recipient"); + expect(cipher4.fields[7].value).toEqual("John"); + expect(cipher4.fields[8].name).toEqual("address_building"); + expect(cipher4.fields[8].value).toEqual("1"); + expect(cipher4.fields[9].name).toEqual("address_apartment"); + expect(cipher4.fields[9].value).toEqual("1"); + expect(cipher4.fields[10].name).toEqual("address_floor"); + expect(cipher4.fields[10].value).toEqual("1"); + expect(cipher4.fields[11].name).toEqual("address_door_code"); + expect(cipher4.fields[11].value).toEqual("123"); + + // website + const cipher5 = result.ciphers.shift(); + expect(cipher5.type).toBe(CipherType.SecureNote); + expect(cipher5.name).toBe("Website"); + + expect(cipher5.fields.length).toBe(3); + expect(cipher5.fields[0].name).toEqual("type"); + expect(cipher5.fields[0].value).toEqual("website"); + expect(cipher5.fields[1].name).toEqual("item_name"); + expect(cipher5.fields[1].value).toEqual("Website"); + expect(cipher5.fields[2].name).toEqual("url"); + expect(cipher5.fields[2].value).toEqual("website.com"); + + // 2nd name/identity + const cipher6 = result.ciphers.shift(); + expect(cipher6.type).toBe(CipherType.SecureNote); + expect(cipher6.name).toBe("Mrs Jane Doe"); + + expect(cipher6.fields.length).toBe(7); + expect(cipher6.fields[0].name).toEqual("type"); + expect(cipher6.fields[0].value).toEqual("name"); + expect(cipher6.fields[1].name).toEqual("title"); + expect(cipher6.fields[1].value).toEqual("Mrs"); + expect(cipher6.fields[2].name).toEqual("first_name"); + expect(cipher6.fields[2].value).toEqual("Jane"); + expect(cipher6.fields[3].name).toEqual("last_name"); + expect(cipher6.fields[3].value).toEqual("Doe"); + expect(cipher6.fields[4].name).toEqual("login"); + expect(cipher6.fields[4].value).toEqual("jdoe"); + expect(cipher6.fields[5].name).toEqual("date_of_birth"); + expect(cipher6.fields[5].value).toEqual("2022-01-30"); + expect(cipher6.fields[6].name).toEqual("place_of_birth"); + expect(cipher6.fields[6].value).toEqual("earth"); + }); + + it("should combine personal information records to one identity if only one identity present", async () => { + const result = await importer.parse(personalInfoData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("MR John Doe"); + expect(cipher.identity.fullName).toBe("MR John Doe"); + expect(cipher.identity.title).toBe("MR"); + expect(cipher.identity.firstName).toBe("John"); + expect(cipher.identity.middleName).toBeNull(); + expect(cipher.identity.lastName).toBe("Doe"); + expect(cipher.identity.username).toBe("jdoe"); + expect(cipher.identity.email).toBe("jdoe@example.com"); + expect(cipher.identity.phone).toBe("+49123123123"); + + expect(cipher.fields.length).toBe(9); + expect(cipher.fields[0].name).toBe("date_of_birth"); + expect(cipher.fields[0].value).toBe("2022-01-30"); + + expect(cipher.fields[1].name).toBe("place_of_birth"); + expect(cipher.fields[1].value).toBe("world"); + + expect(cipher.fields[2].name).toBe("email_type"); + expect(cipher.fields[2].value).toBe("personal"); + + expect(cipher.fields[3].name).toBe("address_recipient"); + expect(cipher.fields[3].value).toBe("John"); + + expect(cipher.fields[4].name).toBe("address_building"); + expect(cipher.fields[4].value).toBe("1"); + + expect(cipher.fields[5].name).toBe("address_apartment"); + expect(cipher.fields[5].value).toBe("1"); + + expect(cipher.fields[6].name).toBe("address_floor"); + expect(cipher.fields[6].value).toBe("1"); + + expect(cipher.fields[7].name).toBe("address_door_code"); + expect(cipher.fields[7].value).toBe("123"); + + expect(cipher.fields[8].name).toBe("url"); + expect(cipher.fields[8].value).toBe("website.com"); + }); +}); diff --git a/jslib/common/spec/importers/firefoxCsvImporter.spec.ts b/jslib/common/spec/importers/firefoxCsvImporter.spec.ts new file mode 100644 index 00000000..e15b86e9 --- /dev/null +++ b/jslib/common/spec/importers/firefoxCsvImporter.spec.ts @@ -0,0 +1,74 @@ +import { FirefoxCsvImporter as Importer } from "jslib-common/importers/firefoxCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { LoginUriView } from "jslib-common/models/view/loginUriView"; +import { LoginView } from "jslib-common/models/view/loginView"; + +import { data as firefoxAccountsData } from "./testData/firefoxCsv/firefoxAccountsData.csv"; +import { data as simplePasswordData } from "./testData/firefoxCsv/simplePasswordData.csv"; + +const CipherData = [ + { + title: "should parse password", + csv: simplePasswordData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "example.com", + login: Object.assign(new LoginView(), { + username: "foo", + password: "bar", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), + notes: null, + type: 1, + }), + }, + { + title: 'should skip "chrome://FirefoxAccounts"', + csv: firefoxAccountsData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "example.com", + login: Object.assign(new LoginView(), { + username: "foo", + password: "bar", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), + notes: null, + type: 1, + }), + }, +]; + +describe("Firefox CSV Importer", () => { + CipherData.forEach((data) => { + it(data.title, async () => { + const importer = new Importer(); + const result = await importer.parse(data.csv); + expect(result != null).toBe(true); + expect(result.ciphers.length).toBeGreaterThan(0); + + const cipher = result.ciphers.shift(); + let property: keyof typeof data.expected; + for (property in data.expected) { + // eslint-disable-next-line + if (data.expected.hasOwnProperty(property)) { + // eslint-disable-next-line + expect(cipher.hasOwnProperty(property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); +}); diff --git a/jslib/common/spec/importers/fsecureFskImporter.spec.ts b/jslib/common/spec/importers/fsecureFskImporter.spec.ts new file mode 100644 index 00000000..63d082df --- /dev/null +++ b/jslib/common/spec/importers/fsecureFskImporter.spec.ts @@ -0,0 +1,77 @@ +import { FSecureFskImporter as Importer } from "jslib-common/importers/fsecureFskImporter"; + +const TestDataWithStyleSetToWebsite: string = JSON.stringify({ + data: { + "8d58b5cf252dd06fbd98f5289e918ab1": { + color: "#00baff", + reatedDate: 1609302913, + creditCvv: "", + creditExpiry: "", + creditNumber: "", + favorite: 0, + modifiedDate: 1609302913, + notes: "note", + password: "word", + passwordList: [], + passwordModifiedDate: 1609302913, + rev: 1, + service: "My first pass", + style: "website", + type: 1, + url: "https://bitwarden.com", + username: "pass", + }, + }, +}); + +const TestDataWithStyleSetToGlobe: string = JSON.stringify({ + data: { + "8d58b5cf252dd06fbd98f5289e918ab1": { + color: "#00baff", + reatedDate: 1609302913, + creditCvv: "", + creditExpiry: "", + creditNumber: "", + favorite: 0, + modifiedDate: 1609302913, + notes: "note", + password: "word", + passwordList: [], + passwordModifiedDate: 1609302913, + rev: 1, + service: "My first pass", + style: "globe", + type: 1, + url: "https://bitwarden.com", + username: "pass", + }, + }, +}); + +describe("FSecure FSK Importer", () => { + it("should parse data with style set to website", async () => { + const importer = new Importer(); + const result = await importer.parse(TestDataWithStyleSetToWebsite); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.login.username).toEqual("pass"); + expect(cipher.login.password).toEqual("word"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://bitwarden.com"); + }); + + it("should parse data with style set to globe", async () => { + const importer = new Importer(); + const result = await importer.parse(TestDataWithStyleSetToGlobe); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.login.username).toEqual("pass"); + expect(cipher.login.password).toEqual("word"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://bitwarden.com"); + }); +}); diff --git a/jslib/common/spec/importers/keepass2XmlImporter.spec.ts b/jslib/common/spec/importers/keepass2XmlImporter.spec.ts new file mode 100644 index 00000000..6b539468 --- /dev/null +++ b/jslib/common/spec/importers/keepass2XmlImporter.spec.ts @@ -0,0 +1,189 @@ +import { KeePass2XmlImporter as Importer } from "jslib-common/importers/keepass2XmlImporter"; + +const TestData = ` + + + KeePass + + 2016-12-31T21:33:52Z + + 2016-12-31T21:33:52Z + + 2016-12-31T21:33:52Z + 365 + + 2016-12-31T21:33:59Z + -1 + -1 + + False + False + True + False + False + + True + AAAAAAAAAAAAAAAAAAAAAA== + 2016-12-31T21:33:52Z + AAAAAAAAAAAAAAAAAAAAAA== + 2016-12-31T21:33:52Z + 10 + 6291456 + AAAAAAAAAAAAAAAAAAAAAA== + AAAAAAAAAAAAAAAAAAAAAA== + + + + + + KvS57lVwl13AfGFLwkvq4Q== + Root + + 48 + + 2016-12-31T21:33:52Z + 2016-12-31T21:33:52Z + 2017-01-01T22:58:00Z + 2016-12-31T21:33:52Z + False + 1 + 2016-12-31T21:33:52Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + P0ParXgGMBW6caOL2YrhqQ== + Folder2 + a note about the folder + 48 + + 2016-12-31T21:43:30Z + 2016-12-31T21:43:43Z + 2017-01-01T22:58:00Z + 2016-12-31T21:43:30Z + False + 1 + 2016-12-31T21:43:43Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + fAa543oYlgnJKkhKag5HLw== + 1 + + + + + + 2016-12-31T21:34:13Z + 2016-12-31T21:40:23Z + 2016-12-31T21:40:23Z + 2016-12-31T21:34:13Z + False + 0 + 2016-12-31T21:43:48Z + + + att2 + att2value + + + attr1 + att1value + +line1 +line2 + + + Notes + This is a note!!! + +line1 +line2 + + + Password + googpass + + + Title + Google + + + URL + google.com + + + UserName + googleuser + + + True + 0 + + + + fAa543oYlgnJKkhKag5HLw== + 0 + + + + + + 2016-12-31T21:34:13Z + 2016-12-31T21:34:40Z + 2016-12-31T21:34:40Z + 2016-12-31T21:34:13Z + False + 0 + 2016-12-31T21:34:40Z + + + Notes + This is a note!!! + +line1 +line2 + + + Password + googpass + + + Title + Google + + + URL + google.com + + + UserName + googleuser + + + True + 0 + + + + + + + + +`; + +describe("KeePass2 Xml Importer", () => { + it("should parse XML data", async () => { + const importer = new Importer(); + const result = await importer.parse(TestData); + expect(result != null).toBe(true); + }); +}); diff --git a/jslib/common/spec/importers/keeperJsonImporter.spec.ts b/jslib/common/spec/importers/keeperJsonImporter.spec.ts new file mode 100644 index 00000000..3da6ac48 --- /dev/null +++ b/jslib/common/spec/importers/keeperJsonImporter.spec.ts @@ -0,0 +1,108 @@ +import { KeeperJsonImporter as Importer } from "jslib-common/importers/keeperImporters/keeperJsonImporter"; +import { Utils } from "jslib-common/misc/utils"; + +import { testData as TestData } from "./testData/keeperJson/testData"; + +describe("Keeper Json Importer", () => { + const testDataJson = JSON.stringify(TestData); + + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse login data", async () => { + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Bank Account 1"); + expect(cipher.login.username).toEqual("customer1234"); + expect(cipher.login.password).toEqual("4813fJDHF4239fdk"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://chase.com"); + expect(cipher.notes).toEqual("These are some notes."); + + const cipher2 = result.ciphers.shift(); + expect(cipher2.name).toEqual("Bank Account 2"); + expect(cipher2.login.username).toEqual("mybankusername"); + expect(cipher2.login.password).toEqual("w4k4k193f$^&@#*%2"); + expect(cipher2.login.uris.length).toEqual(1); + const uriView2 = cipher2.login.uris.shift(); + expect(uriView2.uri).toEqual("https://amex.com"); + expect(cipher2.notes).toEqual("Some great information here."); + + const cipher3 = result.ciphers.shift(); + expect(cipher3.name).toEqual("Some Account"); + expect(cipher3.login.username).toEqual("someUserName"); + expect(cipher3.login.password).toEqual("w4k4k1wergf$^&@#*%2"); + expect(cipher3.notes).toBeNull(); + expect(cipher3.fields).toBeNull(); + expect(cipher3.login.uris.length).toEqual(1); + const uriView3 = cipher3.login.uris.shift(); + expect(uriView3.uri).toEqual("https://example.com"); + }); + + it("should import TOTP when present", async () => { + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.login.totp).toBeNull(); + + // 2nd Cipher + const cipher2 = result.ciphers.shift(); + expect(cipher2.login.totp).toEqual( + "otpauth://totp/Amazon:me@company.com?secret=JBSWY3DPEHPK3PXP&issuer=Amazon&algorithm=SHA1&digits=6&period=30" + ); + }); + + it("should parse custom fields", async () => { + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.fields.length).toBe(1); + expect(cipher.fields[0].name).toEqual("Account Number"); + expect(cipher.fields[0].value).toEqual("123-456-789"); + + // 2nd Cipher + const cipher2 = result.ciphers.shift(); + expect(cipher2.fields.length).toBe(2); + expect(cipher2.fields[0].name).toEqual("Security Group"); + expect(cipher2.fields[0].value).toEqual("Public"); + + expect(cipher2.fields[1].name).toEqual("IP Address"); + expect(cipher2.fields[1].value).toEqual("12.45.67.8"); + }); + + it("should create folders and assigned ciphers to them", async () => { + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const folders = result.folders; + expect(folders.length).toBe(2); + expect(folders[0].name).toBe("Optional Private Folder 1"); + expect(folders[1].name).toBe("My Customer 1"); + + expect(result.folderRelationships[0]).toEqual([0, 0]); + expect(result.folderRelationships[1]).toEqual([1, 0]); + expect(result.folderRelationships[2]).toEqual([1, 1]); + }); + + it("should create collections if part of an organization", async () => { + importer.organizationId = Utils.newGuid(); + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const collections = result.collections; + expect(collections.length).toBe(2); + expect(collections[0].name).toBe("Optional Private Folder 1"); + expect(collections[1].name).toBe("My Customer 1"); + + expect(result.collectionRelationships[0]).toEqual([0, 0]); + expect(result.collectionRelationships[1]).toEqual([1, 0]); + expect(result.collectionRelationships[2]).toEqual([1, 1]); + }); +}); diff --git a/jslib/common/spec/importers/lastpassCsvImporter.spec.ts b/jslib/common/spec/importers/lastpassCsvImporter.spec.ts new file mode 100644 index 00000000..9cca6c4f --- /dev/null +++ b/jslib/common/spec/importers/lastpassCsvImporter.spec.ts @@ -0,0 +1,202 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { LastPassCsvImporter as Importer } from "jslib-common/importers/lastpassCsvImporter"; +import { ImportResult } from "jslib-common/models/domain/importResult"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { FieldView } from "jslib-common/models/view/fieldView"; + +function baseExcept(result: ImportResult) { + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); +} + +function expectLogin(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Login); + + expect(cipher.name).toBe("example.com"); + expect(cipher.notes).toBe("super secure notes"); + expect(cipher.login.uri).toBe("http://example.com"); + expect(cipher.login.username).toBe("someUser"); + expect(cipher.login.password).toBe("myPassword"); + expect(cipher.login.totp).toBe("Y64VEVMBTSXCYIWRSHRNDZW62MPGVU2G"); +} + +const CipherData = [ + { + title: "should parse expiration date", + csv: `url,username,password,extra,name,grouping,fav +http://sn,,,"NoteType:Credit Card +Name on Card:John Doe +Type: +Number:1234567812345678 +Security Code:123 +Start Date:October,2017 +Expiration Date:June,2020 +Notes:some text +",Credit-card,,0`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "Credit-card", + notes: "some text\n", + type: 3, + card: { + cardholderName: "John Doe", + number: "1234567812345678", + code: "123", + expYear: "2020", + expMonth: "6", + }, + fields: [ + Object.assign(new FieldView(), { + name: "Start Date", + value: "October,2017", + type: FieldType.Text, + }), + ], + }), + }, + { + title: "should parse blank card note", + csv: `url,username,password,extra,name,grouping,fav +http://sn,,,"NoteType:Credit Card +Name on Card: +Type: +Number: +Security Code: +Start Date:, +Expiration Date:, +Notes:",empty,,0`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "empty", + notes: null, + type: 3, + card: { + expMonth: undefined, + }, + fields: [ + Object.assign(new FieldView(), { + name: "Start Date", + value: ",", + type: FieldType.Text, + }), + ], + }), + }, + { + title: "should parse card expiration date w/ no exp year", + csv: `url,username,password,extra,name,grouping,fav +http://sn,,,"NoteType:Credit Card +Name on Card:John Doe +Type:Visa +Number:1234567887654321 +Security Code:321 +Start Date:, +Expiration Date:January, +Notes:",noyear,,0`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "noyear", + notes: null, + type: 3, + card: { + cardholderName: "John Doe", + number: "1234567887654321", + code: "321", + expMonth: "1", + }, + fields: [ + Object.assign(new FieldView(), { + name: "Type", + value: "Visa", + type: FieldType.Text, + }), + Object.assign(new FieldView(), { + name: "Start Date", + value: ",", + type: FieldType.Text, + }), + ], + }), + }, + { + title: "should parse card expiration date w/ no month", + csv: `url,username,password,extra,name,grouping,fav +http://sn,,,"NoteType:Credit Card +Name on Card:John Doe +Type:Mastercard +Number:8765432112345678 +Security Code:987 +Start Date:, +Expiration Date:,2020 +Notes:",nomonth,,0`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "nomonth", + notes: null, + type: 3, + card: { + cardholderName: "John Doe", + number: "8765432112345678", + code: "987", + expYear: "2020", + expMonth: undefined, + }, + fields: [ + Object.assign(new FieldView(), { + name: "Type", + value: "Mastercard", + type: FieldType.Text, + }), + Object.assign(new FieldView(), { + name: "Start Date", + value: ",", + type: FieldType.Text, + }), + ], + }), + }, +]; + +describe("Lastpass CSV Importer", () => { + CipherData.forEach((data) => { + it(data.title, async () => { + const importer = new Importer(); + const result = await importer.parse(data.csv); + expect(result != null).toBe(true); + expect(result.ciphers.length).toBeGreaterThan(0); + + const cipher = result.ciphers.shift(); + let property: keyof typeof data.expected; + for (property in data.expected) { + // eslint-disable-next-line + if (data.expected.hasOwnProperty(property)) { + // eslint-disable-next-line + expect(cipher.hasOwnProperty(property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); + + it("should parse login with totp", async () => { + const input = `url,username,password,totp,extra,name,grouping,fav + http://example.com,someUser,myPassword,Y64VEVMBTSXCYIWRSHRNDZW62MPGVU2G,super secure notes,example.com,,0`; + + const importer = new Importer(); + const result = await importer.parse(input); + baseExcept(result); + + const cipher = result.ciphers[0]; + expectLogin(cipher); + }); +}); diff --git a/jslib/common/spec/importers/mykiCsvImporter.spec.ts b/jslib/common/spec/importers/mykiCsvImporter.spec.ts new file mode 100644 index 00000000..d9538ee7 --- /dev/null +++ b/jslib/common/spec/importers/mykiCsvImporter.spec.ts @@ -0,0 +1,633 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { MykiCsvImporter as Importer } from "jslib-common/importers/mykiCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; + +import { userAccountData } from "./testData/mykiCsv/UserAccount.csv"; +import { userCreditCardData } from "./testData/mykiCsv/UserCreditCard.csv"; +import { userIdCardData } from "./testData/mykiCsv/UserIdCard.csv"; +import { userIdentityData } from "./testData/mykiCsv/UserIdentity.csv"; +import { userNoteData } from "./testData/mykiCsv/UserNote.csv"; +import { userTwoFaData } from "./testData/mykiCsv/UserTwofa.csv"; + +function expectDriversLicense(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Joe User's nickname"); + expect(cipher.notes).toBe("Additional information"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("123456"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Driver's License"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("02/02/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("02/02/2024"); +} + +function expectPassport(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Passport ID card"); + expect(cipher.notes).toBe("Additional information field"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.passportNumber).toBe("1234567"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Passport"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectSocialSecurity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Social Security ID card"); + expect(cipher.notes).toBe("Additional information field text"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.ssn).toBe("123455678"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Social Security"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectIdCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("ID card type ID card"); + expect(cipher.notes).toBe("Additional Information field text"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("1234566"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("ID Card"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectTaxNumber(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Tax number ID card"); + expect(cipher.notes).toBe("Additinoal information text field"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12345678"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Tax Number"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectBankAccount(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Bank account ID card"); + expect(cipher.notes).toBe("Additional text information here"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12344556677"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Bank Account"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectInsuranceCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Insurance card ID card"); + expect(cipher.notes).toBe("Additional information text goes here"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("123456677"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Insurance Card"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2022"); +} + +function expectHealthCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Health card Id card"); + expect(cipher.notes).toBe("More info"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("1234670"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Health Card"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectMembershipCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Membership ID card"); + expect(cipher.notes).toBe("Add'l info"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12345709"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Membership"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectDatabase(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Database ID card"); + expect(cipher.notes).toBe("Addin't info"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12345089u"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Database"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectOutdoorLicense(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Outdoor license ID card"); + expect(cipher.notes).toBe("Additional info"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("123890090"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Outdoor License"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectRewardProgram(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Reward program Id card"); + expect(cipher.notes).toBe("1234890"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12345890b"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Reward Program"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectSoftwareLicense(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Software license ID card"); + expect(cipher.notes).toBe( + "It seems like the fields don't change, which makes it pretty useless that they have so many ID card types." + ); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("1234567c"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Software License"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectTourVisa(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Tour visa ID card"); + expect(cipher.notes).toBe("Additional Informaion text"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("123456lkhj"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Tour Visa"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +describe("Myki CSV Importer", () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse userAccount records", async () => { + const result = await importer.parse(userAccountData); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("PasswordNickname"); + expect(cipher.login.username).toEqual("user.name@email.com"); + expect(cipher.login.password).toEqual("abc123"); + expect(cipher.login.totp).toEqual("someTOTPSeed"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("http://www.google.com"); + expect(cipher.notes).toEqual("This is the additional information text."); + + expect(cipher.fields.length).toBe(2); + + expect(cipher.fields[0].name).toBe("status"); + expect(cipher.fields[0].value).toBe("active"); + + expect(cipher.fields[1].name).toBe("tags"); + expect(cipher.fields[1].value).toBe("someTag"); + }); + + it("should parse userTwoFa records", async () => { + const result = await importer.parse(userTwoFaData); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("2FA nickname"); + expect(cipher.login.username).toBeNull(); + expect(cipher.login.password).toBeNull(); + expect(cipher.login.totp).toBe("someTOTPSeed"); + expect(cipher.notes).toEqual("Additional information field content."); + + expect(cipher.fields.length).toBe(2); + + expect(cipher.fields[0].name).toBe("status"); + expect(cipher.fields[0].value).toBe("active"); + + expect(cipher.fields[1].name).toBe("tags"); + expect(cipher.fields[1].value).toBe("someTag"); + }); + + it("should parse creditCard records", async () => { + const result = await importer.parse(userCreditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Card); + expect(cipher.name).toBe("Visa test card"); + expect(cipher.card.brand).toBe("Visa"); + expect(cipher.card.cardholderName).toBe("Joe User"); + expect(cipher.card.number).toBe("4111111111111111"); + expect(cipher.card.code).toBe("222"); + expect(cipher.card.expMonth).toBe("04"); + expect(cipher.card.expYear).toBe("24"); + + expect(cipher.notes).toBe("This is the additional information field"); + + expect(cipher.fields.length).toBe(2); + + expect(cipher.fields[0].name).toBe("status"); + expect(cipher.fields[0].value).toBe("active"); + + expect(cipher.fields[1].name).toBe("tags"); + expect(cipher.fields[1].value).toBe("someTag"); + }); + + it("should parse identity records", async () => { + const result = await importer.parse(userIdentityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Joe User's nickname"); + expect(cipher.identity.fullName).toBe("Mr Joe M User"); + expect(cipher.identity.title).toBe("Mr"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.email).toBe("joe.user@email.com"); + + expect(cipher.identity.address1).toBe("1 Example House"); + expect(cipher.identity.address2).toBe("Suite 300"); + + expect(cipher.identity.city).toBe("Portland"); + expect(cipher.identity.postalCode).toBe("04101"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(4); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toBe("tags"); + expect(cipher.fields[1].value).toBe("someTag"); + + expect(cipher.fields[2].name).toEqual("gender"); + expect(cipher.fields[2].value).toEqual("Male"); + + expect(cipher.fields[3].name).toEqual("number"); + expect(cipher.fields[3].value).toEqual("2223334444"); + }); + + it("should parse secureNote records", async () => { + const result = await importer.parse(userNoteData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.SecureNote); + expect(cipher.name).toBe("The title of a secure note"); + expect(cipher.notes).toBe("The content of a secure note. Lorem ipsum, etc."); + + expect(cipher.fields.length).toBe(1); + + expect(cipher.fields[0].name).toBe("status"); + expect(cipher.fields[0].value).toBe("active"); + }); + + it("should parse idCard records", async () => { + const result = await importer.parse(userIdCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + + expect(result.ciphers.length).toBe(14); + + // Driver's license + const cipher = result.ciphers.shift(); + expectDriversLicense(cipher); + + // Passport + const cipher2 = result.ciphers.shift(); + expectPassport(cipher2); + + // Social Security + const cipher3 = result.ciphers.shift(); + expectSocialSecurity(cipher3); + + // Id Card + const cipher4 = result.ciphers.shift(); + expectIdCard(cipher4); + + // Tax Number + const cipher5 = result.ciphers.shift(); + expectTaxNumber(cipher5); + + // Bank Account + const cipher6 = result.ciphers.shift(); + expectBankAccount(cipher6); + + // Insurance card + const cipher7 = result.ciphers.shift(); + expectInsuranceCard(cipher7); + + // Health card + const cipher8 = result.ciphers.shift(); + expectHealthCard(cipher8); + + // Membership card + const cipher9 = result.ciphers.shift(); + expectMembershipCard(cipher9); + + // Database card + const cipher10 = result.ciphers.shift(); + expectDatabase(cipher10); + + // Outdoor license + const cipher11 = result.ciphers.shift(); + expectOutdoorLicense(cipher11); + + // Reward program + const cipher12 = result.ciphers.shift(); + expectRewardProgram(cipher12); + + // Software license + const cipher13 = result.ciphers.shift(); + expectSoftwareLicense(cipher13); + + // Tour visa + const cipher14 = result.ciphers.shift(); + expectTourVisa(cipher14); + }); +}); diff --git a/jslib/common/spec/importers/nordpassCsvImporter.spec.ts b/jslib/common/spec/importers/nordpassCsvImporter.spec.ts new file mode 100644 index 00000000..4022b7e3 --- /dev/null +++ b/jslib/common/spec/importers/nordpassCsvImporter.spec.ts @@ -0,0 +1,181 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { NordPassCsvImporter as Importer } from "jslib-common/importers/nordpassCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { IdentityView } from "jslib-common/models/view/identityView"; + +import { data as creditCardData } from "./testData/nordpassCsv/nordpass.card.csv"; +import { data as identityData } from "./testData/nordpassCsv/nordpass.identity.csv"; +import { data as loginData } from "./testData/nordpassCsv/nordpass.login.csv"; +import { data as secureNoteData } from "./testData/nordpassCsv/nordpass.secureNote.csv"; + +const namesTestData = [ + { + title: "Given #fullName should set firstName", + fullName: "MyFirstName", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: null, + lastName: null, + }), + }, + { + title: "Given #fullName should set first- and lastName", + fullName: "MyFirstName MyLastName", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: null, + lastName: "MyLastName", + }), + }, + { + title: "Given #fullName should set first-, middle and lastName", + fullName: "MyFirstName MyMiddleName MyLastName", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: "MyMiddleName", + lastName: "MyLastName", + }), + }, + { + title: "Given #fullName should set first-, middle and lastName with Jr", + fullName: "MyFirstName MyMiddleName MyLastName Jr", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: "MyMiddleName", + lastName: "MyLastName Jr", + }), + }, + { + title: "Given #fullName should set first-, middle and lastName with Jr and III", + fullName: "MyFirstName MyMiddleName MyLastName Jr III", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: "MyMiddleName", + lastName: "MyLastName Jr III", + }), + }, +]; + +function expectLogin(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Login); + + expect(cipher.name).toBe("SomeVaultItemName"); + expect(cipher.notes).toBe("Some note for the VaultItem"); + expect(cipher.login.uri).toBe("https://example.com"); + expect(cipher.login.username).toBe("hello@bitwarden.com"); + expect(cipher.login.password).toBe("someStrongPassword"); +} + +function expectCreditCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Card); + + expect(cipher.name).toBe("SomeVisa"); + expect(cipher.card.brand).toBe("Visa"); + expect(cipher.card.cardholderName).toBe("SomeHolder"); + expect(cipher.card.number).toBe("4024007103939509"); + expect(cipher.card.code).toBe("123"); + expect(cipher.card.expMonth).toBe("1"); + expect(cipher.card.expYear).toBe("22"); +} + +function expectIdentity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + + expect(cipher.name).toBe("SomeTitle"); + expect(cipher.identity.fullName).toBe("MyFirstName MyMiddleName MyLastName"); + expect(cipher.identity.firstName).toBe("MyFirstName"); + expect(cipher.identity.middleName).toBe("MyMiddleName"); + expect(cipher.identity.lastName).toBe("MyLastName"); + expect(cipher.identity.email).toBe("hello@bitwarden.com"); + expect(cipher.identity.phone).toBe("123456789"); + + expect(cipher.identity.address1).toBe("Test street 123"); + expect(cipher.identity.address2).toBe("additional addressinfo"); + expect(cipher.identity.postalCode).toBe("123456"); + expect(cipher.identity.city).toBe("Cologne"); + expect(cipher.identity.state).toBe("North-Rhine-Westphalia"); + expect(cipher.identity.country).toBe("GERMANY"); + expect(cipher.notes).toBe("SomeNoteToMyIdentity"); +} + +function expectSecureNote(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.SecureNote); + + expect(cipher.name).toBe("MySuperSecureNoteTitle"); + expect(cipher.secureNote.type).toBe(SecureNoteType.Generic); + expect(cipher.notes).toBe("MySuperSecureNote"); +} + +describe("NordPass CSV Importer", () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse login records", async () => { + const result = await importer.parse(loginData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectLogin(cipher); + }); + + it("should parse credit card records", async () => { + const result = await importer.parse(creditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectCreditCard(cipher); + }); + + it("should parse identity records", async () => { + const result = await importer.parse( + identityData.replace("#fullName", "MyFirstName MyMiddleName MyLastName") + ); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectIdentity(cipher); + }); + + namesTestData.forEach((data) => { + it(data.title.replace("#fullName", data.fullName), async () => { + const result = await importer.parse(identityData.replace("#fullName", data.fullName)); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expect(cipher.identity.firstName).toBe(data.expected.firstName); + expect(cipher.identity.middleName).toBe(data.expected.middleName); + expect(cipher.identity.lastName).toBe(data.expected.lastName); + }); + }); + + it("should parse secureNote records", async () => { + const result = await importer.parse(secureNoteData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectSecureNote(cipher); + }); + + it("should parse an item and create a folder", async () => { + const result = await importer.parse(secureNoteData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.folders.length).toBe(1); + const folder = result.folders[0]; + expect(folder.name).toBe("notesFolder"); + }); +}); diff --git a/jslib/common/spec/importers/onepassword1PifImporter.spec.ts b/jslib/common/spec/importers/onepassword1PifImporter.spec.ts new file mode 100644 index 00000000..ffa2417a --- /dev/null +++ b/jslib/common/spec/importers/onepassword1PifImporter.spec.ts @@ -0,0 +1,527 @@ +import { FieldType } from "jslib-common/enums/fieldType"; +import { OnePassword1PifImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepassword1PifImporter"; + +const TestData: string = + "***aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee***\n" + + JSON.stringify({ + uuid: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + updatedAt: 1486071244, + securityLevel: "SL5", + contentsHash: "aaaaaaaa", + title: "Imported Entry", + location: "https://www.google.com", + secureContents: { + fields: [ + { + value: "user@test.net", + id: "email-input", + name: "email", + type: "T", + designation: "username", + }, + { + value: "myservicepassword", + id: "password-input", + name: "password", + type: "P", + designation: "password", + }, + ], + sections: [ + { + fields: [ + { + k: "concealed", + n: "AAAAAAAAAAAABBBBBBBBBBBCCCCCCCCC", + v: "console-password-123", + t: "console password", + }, + ], + title: "Admin Console", + name: "admin_console", + }, + ], + passwordHistory: [ + { + value: "old-password", + time: 1447791421, + }, + ], + }, + URLs: [ + { + label: "website", + url: "https://www.google.com", + }, + ], + txTimestamp: 1508941334, + createdAt: 1390426636, + typeName: "webforms.WebForm", + }); + +const WindowsOpVaultTestData = JSON.stringify({ + category: "001", + created: 1544823719, + hmac: "NtyBmTTPOb88HV3JUKPx1xl/vcMhac9kvCfe/NtszY0=", + k: "**REMOVED LONG LINE FOR LINTER** -Kyle", + tx: 1553395669, + updated: 1553395669, + uuid: "528AB076FB5F4FBF960884B8E01619AC", + overview: { + title: "Google", + URLs: [ + { + u: "google.com", + }, + ], + url: "google.com", + ps: 26, + ainfo: "googluser", + }, + details: { + passwordHistory: [ + { + value: "oldpass1", + time: 1553394449, + }, + { + value: "oldpass2", + time: 1553394457, + }, + { + value: "oldpass3", + time: 1553394458, + }, + { + value: "oldpass4", + time: 1553394459, + }, + { + value: "oldpass5", + time: 1553394460, + }, + { + value: "oldpass6", + time: 1553394461, + }, + ], + fields: [ + { + type: "T", + id: "username", + name: "username", + value: "googluser", + designation: "username", + }, + { + type: "P", + id: "password", + name: "password", + value: "12345678901", + designation: "password", + }, + ], + notesPlain: "This is a note\r\n\r\nline1\r\nline2", + sections: [ + { + title: "test", + name: "1214FD88CD30405D9EED14BEB4D61B60", + fields: [ + { + k: "string", + n: "6CC3BD77482D4559A4B8BB2D360F821B", + v: "fgfg", + t: "fgggf", + }, + { + k: "concealed", + n: "5CFE7BCAA1DF4578BBF7EB508959BFF3", + v: "dfgdfgfdg", + t: "pwfield", + }, + ], + }, + ], + }, +}); + +const IdentityTestData = JSON.stringify({ + uuid: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + updatedAt: 1553365894, + securityLevel: "SL5", + contentsHash: "eeeeeeee", + title: "Test Identity", + secureContents: { + lastname: "Fritzenberger", + zip: "223344", + birthdate_dd: "11", + homephone: "+49 333 222 111", + company: "Web Inc.", + firstname: "Frank", + birthdate_mm: "3", + country: "de", + sex: "male", + sections: [ + { + fields: [ + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "firstname", + v: "Frank", + a: { + guarded: "yes", + }, + t: "first name", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "initial", + v: "MD", + a: { + guarded: "yes", + }, + t: "initial", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "lastname", + v: "Fritzenberger", + a: { + guarded: "yes", + }, + t: "last name", + }, + { + k: "menu", + v: "male", + n: "sex", + a: { + guarded: "yes", + }, + t: "sex", + }, + { + k: "date", + v: 1552305660, + n: "birthdate", + a: { + guarded: "yes", + }, + t: "birth date", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "occupation", + v: "Engineer", + a: { + guarded: "yes", + }, + t: "occupation", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "company", + v: "Web Inc.", + a: { + guarded: "yes", + }, + t: "company", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "department", + v: "IT", + a: { + guarded: "yes", + }, + t: "department", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "jobtitle", + v: "Developer", + a: { + guarded: "yes", + }, + t: "job title", + }, + ], + title: "Identification", + name: "name", + }, + { + fields: [ + { + k: "address", + inputTraits: { + autocapitalization: "Sentences", + }, + n: "address", + v: { + street: "Mainstreet 1", + city: "Berlin", + country: "de", + zip: "223344", + }, + a: { + guarded: "yes", + }, + t: "address", + }, + { + k: "phone", + v: "+49 001 222 333 44", + n: "defphone", + a: { + guarded: "yes", + }, + t: "default phone", + }, + { + k: "phone", + v: "+49 333 222 111", + n: "homephone", + a: { + guarded: "yes", + }, + t: "home", + }, + { + k: "phone", + n: "cellphone", + a: { + guarded: "yes", + }, + t: "mobile", + }, + { + k: "phone", + n: "busphone", + a: { + guarded: "yes", + }, + t: "business", + }, + ], + title: "Address", + name: "address", + }, + { + fields: [ + { + k: "string", + n: "username", + a: { + guarded: "yes", + }, + t: "username", + }, + { + k: "string", + n: "reminderq", + t: "reminder question", + }, + { + k: "string", + n: "remindera", + t: "reminder answer", + }, + { + k: "string", + inputTraits: { + keyboard: "EmailAddress", + }, + n: "email", + v: "test@web.de", + a: { + guarded: "yes", + }, + t: "email", + }, + { + k: "string", + n: "website", + inputTraits: { + keyboard: "URL", + }, + t: "website", + }, + { + k: "string", + n: "icq", + t: "ICQ", + }, + { + k: "string", + n: "skype", + t: "skype", + }, + { + k: "string", + n: "aim", + t: "AOL/AIM", + }, + { + k: "string", + n: "yahoo", + t: "Yahoo", + }, + { + k: "string", + n: "msn", + t: "MSN", + }, + { + k: "string", + n: "forumsig", + t: "forum signature", + }, + ], + title: "Internet Details", + name: "internet", + }, + { + title: "Related Items", + name: "linked items", + }, + ], + initial: "MD", + address1: "Mainstreet 1", + city: "Berlin", + jobtitle: "Developer", + occupation: "Engineer", + department: "IT", + email: "test@web.de", + birthdate_yy: "2019", + homephone_local: "+49 333 222 111", + defphone_local: "+49 001 222 333 44", + defphone: "+49 001 222 333 44", + }, + txTimestamp: 1553365894, + createdAt: 1553364679, + typeName: "identities.Identity", +}); + +describe("1Password 1Pif Importer", () => { + it("should parse data", async () => { + const importer = new Importer(); + const result = await importer.parse(TestData); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.login.username).toEqual("user@test.net"); + expect(cipher.login.password).toEqual("myservicepassword"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://www.google.com"); + }); + + it('should create concealed field as "hidden" type', async () => { + const importer = new Importer(); + const result = await importer.parse(TestData); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + + const cipher = ciphers.shift(); + const fields = cipher.fields; + expect(fields.length).toEqual(1); + + const field = fields.shift(); + expect(field.name).toEqual("console password"); + expect(field.value).toEqual("console-password-123"); + expect(field.type).toEqual(FieldType.Hidden); + }); + + it("should create identity records", async () => { + const importer = new Importer(); + const result = await importer.parse(IdentityTestData); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Test Identity"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Frank"); + expect(identity.middleName).toEqual("MD"); + expect(identity.lastName).toEqual("Fritzenberger"); + expect(identity.company).toEqual("Web Inc."); + expect(identity.address1).toEqual("Mainstreet 1"); + expect(identity.country).toEqual("DE"); + expect(identity.city).toEqual("Berlin"); + expect(identity.postalCode).toEqual("223344"); + expect(identity.phone).toEqual("+49 001 222 333 44"); + expect(identity.email).toEqual("test@web.de"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(6); + const fields = cipher.fields; + expect(fields[0].name).toEqual("sex"); + expect(fields[0].value).toEqual("male"); + expect(fields[1].name).toEqual("birth date"); + expect(fields[1].value).toEqual("Mon, 11 Mar 2019 12:01:00 GMT"); + expect(fields[2].name).toEqual("occupation"); + expect(fields[2].value).toEqual("Engineer"); + expect(fields[3].name).toEqual("department"); + expect(fields[3].value).toEqual("IT"); + expect(fields[4].name).toEqual("job title"); + expect(fields[4].value).toEqual("Developer"); + expect(fields[5].name).toEqual("home"); + expect(fields[5].value).toEqual("+49 333 222 111"); + }); + + it("should create password history", async () => { + const importer = new Importer(); + const result = await importer.parse(TestData); + const cipher = result.ciphers.shift(); + + expect(cipher.passwordHistory.length).toEqual(1); + const ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("old-password"); + expect(ph.lastUsedDate.toISOString()).toEqual("2015-11-17T20:17:01.000Z"); + }); + + it("should create password history from windows opvault 1pif format", async () => { + const importer = new Importer(); + const result = await importer.parse(WindowsOpVaultTestData); + const cipher = result.ciphers.shift(); + + expect(cipher.passwordHistory.length).toEqual(5); + let ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass6"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:41.000Z"); + ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass5"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:40.000Z"); + ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass4"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:39.000Z"); + ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass3"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:38.000Z"); + ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass2"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:37.000Z"); + }); +}); diff --git a/jslib/common/spec/importers/onepassword1PuxImporter.spec.ts b/jslib/common/spec/importers/onepassword1PuxImporter.spec.ts new file mode 100644 index 00000000..a56e080a --- /dev/null +++ b/jslib/common/spec/importers/onepassword1PuxImporter.spec.ts @@ -0,0 +1,689 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { OnePassword1PuxImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepassword1PuxImporter"; +import { Utils } from "jslib-common/misc/utils"; +import { FieldView } from "jslib-common/models/view/fieldView"; + +import { APICredentialsData } from "./testData/onePassword1Pux/APICredentials"; +import { BankAccountData } from "./testData/onePassword1Pux/BankAccount"; +import { CreditCardData } from "./testData/onePassword1Pux/CreditCard"; +import { DatabaseData } from "./testData/onePassword1Pux/Database"; +import { DriversLicenseData } from "./testData/onePassword1Pux/DriversLicense"; +import { EmailAccountData } from "./testData/onePassword1Pux/EmailAccount"; +import { EmailFieldData } from "./testData/onePassword1Pux/Emailfield"; +import { EmailFieldOnIdentityData } from "./testData/onePassword1Pux/EmailfieldOnIdentity"; +import { EmailFieldOnIdentityPrefilledData } from "./testData/onePassword1Pux/EmailfieldOnIdentity_Prefilled"; +import { IdentityData } from "./testData/onePassword1Pux/IdentityData"; +import { LoginData } from "./testData/onePassword1Pux/LoginData"; +import { MedicalRecordData } from "./testData/onePassword1Pux/MedicalRecord"; +import { MembershipData } from "./testData/onePassword1Pux/Membership"; +import { OnePuxExampleFile } from "./testData/onePassword1Pux/Onepux_example"; +import { OutdoorLicenseData } from "./testData/onePassword1Pux/OutdoorLicense"; +import { PassportData } from "./testData/onePassword1Pux/Passport"; +import { PasswordData } from "./testData/onePassword1Pux/Password"; +import { RewardsProgramData } from "./testData/onePassword1Pux/RewardsProgram"; +import { SSNData } from "./testData/onePassword1Pux/SSN"; +import { SanitizedExport } from "./testData/onePassword1Pux/SanitizedExport"; +import { SecureNoteData } from "./testData/onePassword1Pux/SecureNote"; +import { ServerData } from "./testData/onePassword1Pux/Server"; +import { SoftwareLicenseData } from "./testData/onePassword1Pux/SoftwareLicense"; +import { WirelessRouterData } from "./testData/onePassword1Pux/WirelessRouter"; + +function validateCustomField(fields: FieldView[], fieldName: string, expectedValue: any) { + expect(fields).toBeDefined(); + const customField = fields.find((f) => f.name === fieldName); + expect(customField).toBeDefined(); + + expect(customField.value).toEqual(expectedValue); +} + +describe("1Password 1Pux Importer", () => { + const OnePuxExampleFileJson = JSON.stringify(OnePuxExampleFile); + const LoginDataJson = JSON.stringify(LoginData); + const CreditCardDataJson = JSON.stringify(CreditCardData); + const IdentityDataJson = JSON.stringify(IdentityData); + const SecureNoteDataJson = JSON.stringify(SecureNoteData); + const SanitizedExportJson = JSON.stringify(SanitizedExport); + + it("should parse login data", async () => { + const importer = new Importer(); + const result = await importer.parse(LoginDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("eToro"); + + expect(cipher.login.username).toEqual("username123123123@gmail.com"); + expect(cipher.login.password).toEqual("password!"); + expect(cipher.login.uris.length).toEqual(1); + expect(cipher.login.uri).toEqual("https://www.fakesite.com"); + expect(cipher.login.totp).toEqual("otpseed777"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(3); + validateCustomField(cipher.fields, "terms", "false"); + validateCustomField(cipher.fields, "policies", "true"); + validateCustomField(cipher.fields, "cyqyggt2otns6tbbqtsl6w2ceu", "username123123"); + }); + + it("should parse notes", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.notes).toEqual("This is a note. *bold*! _italic_!"); + }); + + it("should set favourite if favIndex equals 1", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.favorite).toBe(true); + }); + + it("should handle custom boolean fields", async () => { + const importer = new Importer(); + const result = await importer.parse(LoginDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + + const cipher = ciphers.shift(); + expect(cipher.fields[0].name).toEqual("terms"); + expect(cipher.fields[0].value).toEqual("false"); + expect(cipher.fields[0].type).toBe(FieldType.Boolean); + + expect(cipher.fields[1].name).toEqual("policies"); + expect(cipher.fields[1].value).toEqual("true"); + expect(cipher.fields[1].type).toBe(FieldType.Boolean); + }); + + it("should add fields of type email as custom fields", async () => { + const importer = new Importer(); + const EmailFieldDataJson = JSON.stringify(EmailFieldData); + const result = await importer.parse(EmailFieldDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + const cipher = ciphers.shift(); + + expect(cipher.fields[0].name).toEqual("reg_email"); + expect(cipher.fields[0].value).toEqual("kriddler@nullvalue.test"); + expect(cipher.fields[0].type).toBe(FieldType.Text); + + expect(cipher.fields[1].name).toEqual("provider"); + expect(cipher.fields[1].value).toEqual("myEmailProvider"); + expect(cipher.fields[1].type).toBe(FieldType.Text); + }); + + it('should create concealed field as "hidden" type', async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + + const cipher = ciphers.shift(); + const fields = cipher.fields; + expect(fields.length).toEqual(1); + + const field = fields.shift(); + expect(field.name).toEqual("PIN"); + expect(field.value).toEqual("12345"); + expect(field.type).toEqual(FieldType.Hidden); + }); + + it("should create password history", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + const cipher = result.ciphers.shift(); + + expect(cipher.passwordHistory.length).toEqual(1); + const ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("12345password"); + expect(ph.lastUsedDate.toISOString()).toEqual("2016-03-18T17:32:35.000Z"); + }); + + it("should create credit card records", async () => { + const importer = new Importer(); + const result = await importer.parse(CreditCardDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Parent's Credit Card"); + expect(cipher.notes).toEqual("My parents' credit card."); + + const card = cipher.card; + expect(card.cardholderName).toEqual("Fred Engels"); + expect(card.number).toEqual("6011111111111117"); + expect(card.code).toEqual("1312"); + expect(card.brand).toEqual("Discover"); + expect(card.expMonth).toEqual("12"); + expect(card.expYear).toEqual("2099"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(12); + validateCustomField(cipher.fields, "txbzvwzpck7ejhfres3733rbpm", "card"); + validateCustomField(cipher.fields, "cashLimit", "$500"); + validateCustomField(cipher.fields, "creditLimit", "$1312"); + validateCustomField(cipher.fields, "validFrom", "200101"); + validateCustomField(cipher.fields, "bank", "Some bank"); + validateCustomField(cipher.fields, "phoneLocal", "123456"); + validateCustomField(cipher.fields, "phoneTollFree", "0800123456"); + validateCustomField(cipher.fields, "phoneIntl", "+49123456"); + validateCustomField(cipher.fields, "website", "somebank.com"); + validateCustomField(cipher.fields, "pin", "1234"); + validateCustomField(cipher.fields, "interest", "1%"); + validateCustomField(cipher.fields, "issuenumber", "123456"); + }); + + it("should create identity records", async () => { + const importer = new Importer(); + const result = await importer.parse(IdentityDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("George Engels"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("George"); + expect(identity.middleName).toEqual("S"); + expect(identity.lastName).toEqual("Engels"); + expect(identity.company).toEqual("Acme Inc."); + expect(identity.address1).toEqual("1312 Main St."); + expect(identity.country).toEqual("US"); + expect(identity.state).toEqual("California"); + expect(identity.city).toEqual("Atlantis"); + expect(identity.postalCode).toEqual("90210"); + expect(identity.phone).toEqual("4565555555"); + expect(identity.email).toEqual("gengels@nullvalue.test"); + expect(identity.username).toEqual("gengels"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(17); + validateCustomField(cipher.fields, "sex", "male"); + validateCustomField(cipher.fields, "birthdate", "Thu, 01 Jan 1981 12:01:00 GMT"); + validateCustomField(cipher.fields, "occupation", "Steel Worker"); + validateCustomField(cipher.fields, "department", "QA"); + validateCustomField(cipher.fields, "jobtitle", "Quality Assurance Manager"); + validateCustomField(cipher.fields, "homephone", "4575555555"); + validateCustomField(cipher.fields, "cellphone", "4585555555"); + validateCustomField(cipher.fields, "busphone", "4595555555"); + validateCustomField(cipher.fields, "reminderq", "Who's a super cool guy?"); + validateCustomField(cipher.fields, "remindera", "Me, buddy."); + validateCustomField(cipher.fields, "website", "cv.gengels.nullvalue.test"); + validateCustomField(cipher.fields, "icq", "12345678"); + validateCustomField(cipher.fields, "skype", "skypeisbad1619"); + validateCustomField(cipher.fields, "aim", "aollol@lololol.aol.com"); + validateCustomField(cipher.fields, "yahoo", "sk8rboi13@yah00.com"); + validateCustomField(cipher.fields, "msn", "msnothankyou@msn&m&m.com"); + validateCustomField(cipher.fields, "forumsig", "super cool guy"); + }); + + it("emails fields on identity types should be added to the identity email field", async () => { + const importer = new Importer(); + const EmailFieldOnIdentityDataJson = JSON.stringify(EmailFieldOnIdentityData); + const result = await importer.parse(EmailFieldOnIdentityDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + const cipher = ciphers.shift(); + + const identity = cipher.identity; + expect(identity.email).toEqual("gengels@nullvalue.test"); + + expect(cipher.fields[0].name).toEqual("provider"); + expect(cipher.fields[0].value).toEqual("myEmailProvider"); + expect(cipher.fields[0].type).toBe(FieldType.Text); + }); + + it("emails fields on identity types should be added to custom fields if identity.email has been filled", async () => { + const importer = new Importer(); + const EmailFieldOnIdentityPrefilledDataJson = JSON.stringify(EmailFieldOnIdentityPrefilledData); + const result = await importer.parse(EmailFieldOnIdentityPrefilledDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + const cipher = ciphers.shift(); + + const identity = cipher.identity; + expect(identity.email).toEqual("gengels@nullvalue.test"); + + expect(cipher.fields[0].name).toEqual("2nd_email"); + expect(cipher.fields[0].value).toEqual("kriddler@nullvalue.test"); + expect(cipher.fields[0].type).toBe(FieldType.Text); + + expect(cipher.fields[1].name).toEqual("provider"); + expect(cipher.fields[1].value).toEqual("myEmailProvider"); + expect(cipher.fields[1].type).toBe(FieldType.Text); + }); + + it("should parse category 005 - Password (Legacy)", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(PasswordData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("SuperSecret Password"); + expect(cipher.notes).toEqual("SuperSecret Password Notes"); + + expect(cipher.login.password).toEqual("GBq[AGb]4*Si3tjwuab^"); + expect(cipher.login.uri).toEqual("https://n0t.y0ur.n0rm4l.w3bs1t3"); + }); + + it("should parse category 100 - SoftwareLicense", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(SoftwareLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Limux Product Key"); + expect(cipher.notes).toEqual("My Software License"); + + expect(cipher.fields.length).toEqual(13); + validateCustomField(cipher.fields, "product_version", "5.10.1000"); + validateCustomField(cipher.fields, "reg_code", "265453-13457355-847327"); + validateCustomField(cipher.fields, "reg_name", "Kay Riddler"); + validateCustomField(cipher.fields, "reg_email", "kriddler@nullvalue.test"); + validateCustomField(cipher.fields, "company", "Riddles and Jigsaw Puzzles GmbH"); + validateCustomField( + cipher.fields, + "download_link", + "https://limuxcompany.nullvalue.test/5.10.1000/isos" + ); + validateCustomField(cipher.fields, "publisher_name", "Limux Software and Hardware"); + validateCustomField(cipher.fields, "publisher_website", "https://limuxcompany.nullvalue.test/"); + validateCustomField(cipher.fields, "retail_price", "$999"); + validateCustomField(cipher.fields, "support_email", "support@nullvalue.test"); + validateCustomField(cipher.fields, "order_date", "Thu, 01 Apr 2021 12:01:00 GMT"); + validateCustomField(cipher.fields, "order_number", "594839"); + validateCustomField(cipher.fields, "order_total", "$1086.59"); + }); + + it("should parse category 101 - BankAccount", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(BankAccountData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.Card); + expect(cipher.name).toEqual("Bank Account"); + expect(cipher.notes).toEqual("My Bank Account"); + + expect(cipher.card.cardholderName).toEqual("Cool Guy"); + + expect(cipher.fields.length).toEqual(9); + validateCustomField(cipher.fields, "bankName", "Super Credit Union"); + validateCustomField(cipher.fields, "accountType", "checking"); + validateCustomField(cipher.fields, "routingNo", "111000999"); + validateCustomField(cipher.fields, "accountNo", "192837465918273645"); + validateCustomField(cipher.fields, "swift", "123456"); + validateCustomField(cipher.fields, "iban", "DE12 123456"); + validateCustomField(cipher.fields, "telephonePin", "5555"); + validateCustomField(cipher.fields, "branchPhone", "9399399933"); + validateCustomField(cipher.fields, "branchAddress", "1 Fifth Avenue"); + }); + + it("should parse category 102 - Database", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(DatabaseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Database"); + expect(cipher.notes).toEqual("My Database"); + + const login = cipher.login; + expect(login.username).toEqual("cooldbuser"); + expect(login.password).toEqual("^+kTjhLaN7wVPAhGU)*J"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "database_type", "postgresql"); + validateCustomField(cipher.fields, "hostname", "my.secret.db.server"); + validateCustomField(cipher.fields, "port", "1337"); + validateCustomField(cipher.fields, "database", "user_database"); + validateCustomField(cipher.fields, "sid", "ASDIUFU-283234"); + validateCustomField(cipher.fields, "alias", "cdbu"); + validateCustomField(cipher.fields, "options", "ssh"); + }); + + it("should parse category 103 - Drivers license", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(DriversLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Michael Scarn"); + expect(cipher.subTitle).toEqual("Michael Scarn"); + expect(cipher.notes).toEqual("My Driver's License"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Michael"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Scarn"); + expect(identity.address1).toEqual("2120 Mifflin Rd."); + expect(identity.state).toEqual("Pennsylvania"); + expect(identity.country).toEqual("United States"); + expect(identity.licenseNumber).toEqual("12345678901"); + + expect(cipher.fields.length).toEqual(6); + validateCustomField(cipher.fields, "birthdate", "Sun, 01 Jan 1978 12:01:00 GMT"); + validateCustomField(cipher.fields, "sex", "male"); + validateCustomField(cipher.fields, "height", "5'11\""); + validateCustomField(cipher.fields, "class", "C"); + validateCustomField(cipher.fields, "conditions", "B"); + validateCustomField(cipher.fields, "expiry_date", "203012"); + }); + + it("should parse category 104 - Outdoor License", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(OutdoorLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Harvest License"); + expect(cipher.subTitle).toEqual("Cash Bandit"); + expect(cipher.notes).toEqual("My Outdoor License"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Cash"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Bandit"); + expect(identity.state).toEqual("Washington"); + expect(identity.country).toEqual("United States of America"); + + expect(cipher.fields.length).toEqual(4); + validateCustomField(cipher.fields, "valid_from", "Thu, 01 Apr 2021 12:01:00 GMT"); + validateCustomField(cipher.fields, "expires", "Fri, 01 Apr 2044 12:01:00 GMT"); + validateCustomField(cipher.fields, "game", "Bananas,blueberries,corn"); + validateCustomField(cipher.fields, "quota", "100/each"); + }); + + it("should parse category 105 - Membership", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(MembershipData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Library Card"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("George"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Engels"); + expect(identity.company).toEqual("National Public Library"); + expect(identity.phone).toEqual("9995555555"); + + expect(cipher.fields.length).toEqual(5); + validateCustomField(cipher.fields, "website", "https://npl.nullvalue.gov.test"); + validateCustomField(cipher.fields, "member_since", "199901"); + validateCustomField(cipher.fields, "expiry_date", "203412"); + validateCustomField(cipher.fields, "membership_no", "64783862"); + validateCustomField(cipher.fields, "pin", "19191"); + }); + + it("should parse category 106 - Passport", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(PassportData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Mr. Globewide"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("David"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Global"); + expect(identity.passportNumber).toEqual("76436847"); + + expect(cipher.fields.length).toEqual(8); + validateCustomField(cipher.fields, "type", "US Passport"); + validateCustomField(cipher.fields, "sex", "female"); + validateCustomField(cipher.fields, "nationality", "International"); + validateCustomField(cipher.fields, "issuing_authority", "Department of State"); + validateCustomField(cipher.fields, "birthdate", "Fri, 01 Apr 1983 12:01:00 GMT"); + validateCustomField(cipher.fields, "birthplace", "A cave somewhere in Maine"); + validateCustomField(cipher.fields, "issue_date", "Wed, 01 Jan 2020 12:01:00 GMT"); + validateCustomField(cipher.fields, "expiry_date", "Sat, 01 Jan 2050 12:01:00 GMT"); + }); + + it("should parse category 107 - RewardsProgram", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(RewardsProgramData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Retail Reward Thing"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Chef"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Coldroom"); + expect(identity.company).toEqual("Super Cool Store Co."); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "membership_no", "member-29813569"); + validateCustomField(cipher.fields, "pin", "99913"); + validateCustomField(cipher.fields, "additional_no", "additional member id"); + validateCustomField(cipher.fields, "member_since", "202101"); + validateCustomField(cipher.fields, "customer_service_phone", "123456"); + validateCustomField(cipher.fields, "reservations_phone", "123456"); + validateCustomField(cipher.fields, "website", "supercoolstore.com"); + }); + + it("should parse category 108 - SSN", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(SSNData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("SSN"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Jack"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Judd"); + expect(identity.ssn).toEqual("131-216-1900"); + }); + + it("should parse category 109 - WirelessRouter", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(WirelessRouterData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Wireless Router"); + expect(cipher.notes).toEqual("My Wifi Router Config"); + + expect(cipher.login.password).toEqual("BqatGTVQ9TCN72tLbjrsHqkb"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "name", "pixel 2Xl"); + validateCustomField(cipher.fields, "server", "127.0.0.1"); + validateCustomField(cipher.fields, "airport_id", "some airportId"); + validateCustomField(cipher.fields, "network_name", "some network name"); + validateCustomField(cipher.fields, "wireless_security", "WPA"); + validateCustomField(cipher.fields, "wireless_password", "wifipassword"); + validateCustomField(cipher.fields, "disk_password", "diskpassword"); + }); + + it("should parse category 110 - Server", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(ServerData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Super Cool Server"); + expect(cipher.notes).toEqual("My Server"); + + expect(cipher.login.username).toEqual("frankly-notsure"); + expect(cipher.login.password).toEqual("*&YHJI87yjy78u"); + expect(cipher.login.uri).toEqual("https://coolserver.nullvalue.test"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField( + cipher.fields, + "admin_console_url", + "https://coolserver.nullvalue.test/admin" + ); + validateCustomField(cipher.fields, "admin_console_username", "frankly-idontknowwhatimdoing"); + validateCustomField(cipher.fields, "admin_console_password", "^%RY&^YUiju8iUYHJI(U"); + validateCustomField(cipher.fields, "name", "Private Hosting Provider Inc."); + validateCustomField(cipher.fields, "website", "https://phpi.nullvalue.test"); + validateCustomField( + cipher.fields, + "support_contact_url", + "https://phpi.nullvalue.test/support" + ); + validateCustomField(cipher.fields, "support_contact_phone", "8882569382"); + }); + + it("should parse category 111 - EmailAccount", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(EmailAccountData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Email Config"); + expect(cipher.notes).toEqual("My Email Config"); + + expect(cipher.fields.length).toEqual(17); + validateCustomField(cipher.fields, "pop_type", "either"); + validateCustomField(cipher.fields, "pop_username", "someuser@nullvalue.test"); + validateCustomField(cipher.fields, "pop_server", "mailserver.nullvalue.test"); + validateCustomField(cipher.fields, "pop_port", "587"); + validateCustomField(cipher.fields, "pop_password", "u1jsf { + const importer = new Importer(); + const jsonString = JSON.stringify(APICredentialsData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("API Credential"); + expect(cipher.notes).toEqual("My API Credential"); + + expect(cipher.login.username).toEqual("apiuser@nullvalue.test"); + expect(cipher.login.password).toEqual("apiapiapiapiapiapiappy"); + expect(cipher.login.uri).toEqual("http://not.your.everyday.hostname"); + + expect(cipher.fields.length).toEqual(4); + validateCustomField(cipher.fields, "type", "jwt"); + validateCustomField(cipher.fields, "filename", "filename.jwt"); + validateCustomField(cipher.fields, "validFrom", "Mon, 04 Apr 2011 12:01:00 GMT"); + validateCustomField(cipher.fields, "expires", "Tue, 01 Apr 2031 12:01:00 GMT"); + }); + + it("should create secure notes", async () => { + const importer = new Importer(); + const result = await importer.parse(SecureNoteDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("Secure Note #1"); + expect(cipher.notes).toEqual( + "This is my secure note. \n\nLorem ipsum expecto patronum. \nThe quick brown fox jumped over the lazy dog." + ); + expect(cipher.secureNote.type).toEqual(SecureNoteType.Generic); + }); + + it("should parse category 113 - Medical Record", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(MedicalRecordData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Some Health Record"); + expect(cipher.notes).toEqual("Some notes about my medical history"); + expect(cipher.secureNote.type).toEqual(SecureNoteType.Generic); + + expect(cipher.fields.length).toEqual(8); + validateCustomField(cipher.fields, "date", "Sat, 01 Jan 2022 12:01:00 GMT"); + validateCustomField(cipher.fields, "location", "some hospital/clinic"); + validateCustomField(cipher.fields, "healthcareprofessional", "Some Doctor"); + validateCustomField(cipher.fields, "patient", "Me"); + validateCustomField(cipher.fields, "reason", "unwell"); + validateCustomField(cipher.fields, "medication", "Insuline"); + validateCustomField(cipher.fields, "dosage", "1"); + validateCustomField(cipher.fields, "notes", "multiple times a day"); + }); + + it("should create folders", async () => { + const importer = new Importer(); + const result = await importer.parse(SanitizedExportJson); + expect(result != null).toBe(true); + + const folders = result.folders; + expect(folders.length).toBe(5); + expect(folders[0].name).toBe("Movies"); + expect(folders[1].name).toBe("Finance"); + expect(folders[2].name).toBe("Travel"); + expect(folders[3].name).toBe("Education"); + expect(folders[4].name).toBe("Starter Kit"); + + // Check that ciphers have a folder assigned to them + expect(result.ciphers.filter((c) => c.folderId === folders[0].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[1].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[2].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[3].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[4].id).length).toBeGreaterThan(0); + }); + + it("should create collections if part of an organization", async () => { + const importer = new Importer(); + importer.organizationId = Utils.newGuid(); + const result = await importer.parse(SanitizedExportJson); + expect(result != null).toBe(true); + + const collections = result.collections; + expect(collections.length).toBe(5); + expect(collections[0].name).toBe("Movies"); + expect(collections[1].name).toBe("Finance"); + expect(collections[2].name).toBe("Travel"); + expect(collections[3].name).toBe("Education"); + expect(collections[4].name).toBe("Starter Kit"); + }); +}); diff --git a/jslib/common/spec/importers/onepasswordMacCsvImporter.spec.ts b/jslib/common/spec/importers/onepasswordMacCsvImporter.spec.ts new file mode 100644 index 00000000..73569fc6 --- /dev/null +++ b/jslib/common/spec/importers/onepasswordMacCsvImporter.spec.ts @@ -0,0 +1,74 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { OnePasswordMacCsvImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepasswordMacCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; + +import { data as creditCardData } from "./testData/onePasswordCsv/creditCard.mac.csv"; +import { data as identityData } from "./testData/onePasswordCsv/identity.mac.csv"; +import { data as multiTypeData } from "./testData/onePasswordCsv/multipleItems.mac.csv"; + +function expectIdentity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + + expect(cipher.identity).toEqual( + expect.objectContaining({ + firstName: "first name", + middleName: "mi", + lastName: "last name", + username: "userNam3", + company: "bitwarden", + phone: "8005555555", + email: "email@bitwarden.com", + }) + ); + + expect(cipher.notes).toContain("address\ncity state zip\nUnited States"); +} + +function expectCreditCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Card); + + expect(cipher.card).toEqual( + expect.objectContaining({ + number: "4111111111111111", + code: "111", + cardholderName: "test", + expMonth: "1", + expYear: "2030", + }) + ); +} + +describe("1Password mac CSV Importer", () => { + it("should parse identity records", async () => { + const importer = new Importer(); + const result = await importer.parse(identityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectIdentity(cipher); + }); + + it("should parse credit card records", async () => { + const importer = new Importer(); + const result = await importer.parse(creditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectCreditCard(cipher); + }); + + it("should parse csv's with multiple record type", async () => { + const importer = new Importer(); + const result = await importer.parse(multiTypeData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(4); + expectIdentity(result.ciphers[1]); + expectCreditCard(result.ciphers[2]); + }); +}); diff --git a/jslib/common/spec/importers/onepasswordWinCsvImporter.spec.ts b/jslib/common/spec/importers/onepasswordWinCsvImporter.spec.ts new file mode 100644 index 00000000..bfc1044a --- /dev/null +++ b/jslib/common/spec/importers/onepasswordWinCsvImporter.spec.ts @@ -0,0 +1,87 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { OnePasswordWinCsvImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepasswordWinCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { FieldView } from "jslib-common/models/view/fieldView"; + +import { data as creditCardData } from "./testData/onePasswordCsv/creditCard.windows.csv"; +import { data as identityData } from "./testData/onePasswordCsv/identity.windows.csv"; +import { data as multiTypeData } from "./testData/onePasswordCsv/multipleItems.windows.csv"; + +function expectIdentity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + + expect(cipher.identity).toEqual( + expect.objectContaining({ + firstName: "first name", + middleName: "mi", + lastName: "last name", + username: "userNam3", + company: "bitwarden", + phone: "8005555555", + email: "email@bitwarden.com", + }) + ); + + expect(cipher.fields).toEqual( + expect.arrayContaining([ + Object.assign(new FieldView(), { + type: FieldType.Text, + name: "address", + value: "address city state zip us", + }), + ]) + ); +} + +function expectCreditCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Card); + + expect(cipher.card).toEqual( + expect.objectContaining({ + number: "4111111111111111", + code: "111", + cardholderName: "test", + expMonth: "1", + expYear: "1970", + }) + ); +} + +describe("1Password windows CSV Importer", () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse identity records", async () => { + const result = await importer.parse(identityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectIdentity(cipher); + }); + + it("should parse credit card records", async () => { + const result = await importer.parse(creditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectCreditCard(cipher); + }); + + it("should parse csv's with multiple record types", async () => { + const result = await importer.parse(multiTypeData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(4); + + expectIdentity(result.ciphers[1]); + expectCreditCard(result.ciphers[2]); + }); +}); diff --git a/jslib/common/spec/importers/safariCsvImporter.spec.ts b/jslib/common/spec/importers/safariCsvImporter.spec.ts new file mode 100644 index 00000000..f1e1186f --- /dev/null +++ b/jslib/common/spec/importers/safariCsvImporter.spec.ts @@ -0,0 +1,74 @@ +import { SafariCsvImporter as Importer } from "jslib-common/importers/safariCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { LoginUriView } from "jslib-common/models/view/loginUriView"; +import { LoginView } from "jslib-common/models/view/loginView"; + +import { data as oldSimplePasswordData } from "./testData/safariCsv/oldSimplePasswordData.csv"; +import { data as simplePasswordData } from "./testData/safariCsv/simplePasswordData.csv"; + +const CipherData = [ + { + title: "should parse URLs in new CSV format", + csv: simplePasswordData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "example.com (example_user)", + login: Object.assign(new LoginView(), { + username: "example_user", + password: "example_p@ssword", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + totp: "otpauth://totp/test?secret=examplesecret", + }), + notes: "Example note\nMore notes on new line", + type: 1, + }), + }, + { + title: "should parse URLs in old CSV format", + csv: oldSimplePasswordData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "example.com (example_user)", + login: Object.assign(new LoginView(), { + username: "example_user", + password: "example_p@ssword", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), + type: 1, + }), + }, +]; + +describe("Safari CSV Importer", () => { + CipherData.forEach((data) => { + it(data.title, async () => { + const importer = new Importer(); + const result = await importer.parse(data.csv); + expect(result != null).toBe(true); + expect(result.ciphers.length).toBeGreaterThan(0); + + const cipher = result.ciphers.shift(); + let property: keyof typeof data.expected; + for (property in data.expected) { + // eslint-disable-next-line + if (data.expected.hasOwnProperty(property)) { + // eslint-disable-next-line + expect(cipher.hasOwnProperty(property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); +}); diff --git a/jslib/common/spec/importers/testData/bitwardenJson/empty.json.ts b/jslib/common/spec/importers/testData/bitwardenJson/empty.json.ts new file mode 100644 index 00000000..43ab8c76 --- /dev/null +++ b/jslib/common/spec/importers/testData/bitwardenJson/empty.json.ts @@ -0,0 +1 @@ +export const data = '{"encrypted":false,"folders":[],"items":[]}'; diff --git a/jslib/common/spec/importers/testData/bitwardenJson/passwordProtected.json.ts b/jslib/common/spec/importers/testData/bitwardenJson/passwordProtected.json.ts new file mode 100644 index 00000000..0462e834 --- /dev/null +++ b/jslib/common/spec/importers/testData/bitwardenJson/passwordProtected.json.ts @@ -0,0 +1,9 @@ +export const data = `{ + "encrypted": true, + "passwordProtected": true, + "salt": "Oy0xcgVRzxQ+9NpB5GLehw==", + "kdfIterations": 100000, + "kdfType": 0, + "encKeyValidation_DO_NOT_EDIT": "2.sZs4Jc1HW9rhABzRRYR/gQ==|8kTDaDxafulnybpWoqVX8RAybhVRTr+dffNjms271Y7amQmIE1VSMwLbk+b2vxZb|IqOo6oXQtmv/Xb/GHDi42XG9c9ILePYtP5qq584VWcg=", + "data": "2.D0AXAf7G/XIwq6EC7A0Suw==|4w+m0wHRo25y1T1Syh5wdAUyF8voqEy54waMEsbnK0Nzee959w54ru5D1NntvxZL4HFqkQLyR6jCFkn5g40f+MGJgihS/wvf4NcJJfLiiFo6MEDOQNBkxw7ZBGuHiKfVuBO5u36JgzQtZ8lyFaduGxFszuF5c+URiE9PDh9jY0//poVgHKwuLZuYFIW+f7h6T+shUWK0ya11lcHn/B/CA2xiI+YiKdNZreJrwN0yslpJ/f+MrOzagvftRjt0GNkwveCtwcYUw/zFvqvibUpKeHcRiXs8SaGoHJ5RTm69FbJ7C5tnLwoVT89Af156uvRAXV7yAC4oPcbU/3TGb6hqYosvi1QNyaqG3M9gxS6+AK0C4yWuNbMLDEr+MWiw0SWLVMKQEkCZ4oM+oTCx52otW3+2V9I8Pv3KmmhkvVvE4wBdweOJeRX53Tf5ySkmpIhCfzj6JMmxO+nmTXIhWnJChr4hPVh+ixv1GQK5thIPTCMXmAtXoTIFUx1KWjS6LjOdi2hKQueVI+XZjf0qnY2vTMxRg0ZsLBA2znQTx+DSEqumORb5T/lV73pWZiCNePSAE2msOm7tep+lm4O/VCViCfXjITAY196syhOK0XnhxJvPALchZY8sYRAfuw6hHoDiVr+JUieRoI7eUrhXBp+D6Py9TL/dS/rHe+C2Zhx+xwx2NfGt+xEp8ZAOOCxgZ0UTeSA/abm0Oz7tJIK1n26acQrgbr7rMeBymAX+5L5OWlwI1hGgEBfj6W0rrbSXf3VMfaFXZ5UsXi1VhzQmU3LyWENoDeImXFQj6zMbUSfcVwLsG5Fg8Ee/kO/wJPfG5BO51+/vFqQj6AkaMEcwg5xNrObHYfQ/DMhIn7YDM2zdzbNTdhnobGkz6YRKFPCgFe3EmIEPEpeh9S3eKE9C7MQsrR8jVSiseR/FipJLsN+W7iOwzeXdwxUFlC/0a98bTKvdrbMgNi6ZVXykHY/t2UyEGpxZGTHoZwhX01kiQrwzC4/+v/676ldxPluO9GY7MtrLveCDsiyBz15u43IGHayDEBNT0rqrOKLYmfzwCWoahRLZQrSmepe/FXqgPqRfyWc/Ro+w3sT9dXUkx3B5xxWgSyABowPV48yBUSJuefhKTpqgzkU+LzhNnWHjnxJzzQ2/|IhlRjnyhIoDM85qHX/bY2zaIU5YaRO/iFVTQDd3uFDo=" +}`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/credentials.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/credentials.csv.ts new file mode 100644 index 00000000..78e42cb1 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/credentials.csv.ts @@ -0,0 +1,2 @@ +export const credentialsData = `username,username2,username3,title,password,note,url,category,otpSecret +jdoe,,,example.com,somePassword,some note for example.com,https://www.example.com,Entertainment,someTOTPSeed`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/id.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/id.csv.ts new file mode 100644 index 00000000..1c939557 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/id.csv.ts @@ -0,0 +1,6 @@ +export const identityData = `type,number,name,issue_date,expiration_date,place_of_issue,state +card,123123123,John Doe,2022-1-30,2032-1-30,, +passport,123123123,John Doe,2022-1-30,2032-1-30,somewhere in Germany, +license,1234556,John Doe,2022-8-10,2022-10-10,,DC +social_security,123123123,John Doe,,,, +tax_number,123123123,,,,,`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/multiplePersonalInfo.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/multiplePersonalInfo.csv.ts new file mode 100644 index 00000000..cca5748b --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/multiplePersonalInfo.csv.ts @@ -0,0 +1,7 @@ +export const multiplePersonalInfoData = `type,title,first_name,middle_name,last_name,login,date_of_birth,place_of_birth,email,email_type,item_name,phone_number,address,country,state,city,zip,address_recipient,address_building,address_apartment,address_floor,address_door_code,job_title,url +name,MR,John,,Doe,jdoe,2022-01-30,world,,,,,,,,,,,,,,,, +email,,,,,,,,jdoe@example.com,personal,Johns email,,,,,,,,,,,,, +number,,,,,,,,,,John's number,+49123123123,,,,,,,,,,,, +address,,,,,,,,,,John's home address,,1 some street,de,DE-0-NW,some city,123123,John,1,1,1,123,, +website,,,,,,,,,,Website,,,,,,,,,,,,,website.com +name,Mrs,Jane,,Doe,jdoe,2022-01-30,earth,,,,,,,,,,,,,,,,`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/payments.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/payments.csv.ts new file mode 100644 index 00000000..336986c1 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/payments.csv.ts @@ -0,0 +1,3 @@ +export const paymentsData = `type,account_name,account_holder,cc_number,code,expiration_month,expiration_year,routing_number,account_number,country,issuing_bank +bank,John's savings account,John Doe,,,,,routingNumber,accountNumber,US,US-ALLY +credit_card,John Doe,,41111111111111111,123,01,2023,,,US,`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/personalInfo.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/personalInfo.csv.ts new file mode 100644 index 00000000..ec99f800 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/personalInfo.csv.ts @@ -0,0 +1,6 @@ +export const personalInfoData = `type,title,first_name,middle_name,last_name,login,date_of_birth,place_of_birth,email,email_type,item_name,phone_number,address,country,state,city,zip,address_recipient,address_building,address_apartment,address_floor,address_door_code,job_title,url +name,MR,John,,Doe,jdoe,2022-01-30,world,,,,,,,,,,,,,,,, +email,,,,,,,,jdoe@example.com,personal,Johns email,,,,,,,,,,,,, +number,,,,,,,,,,John's number,+49123123123,,,,,,,,,,,, +address,,,,,,,,,,John's home address,,1 some street,de,DE-0-NW,some city,123123,John,1,1,1,123,, +website,,,,,,,,,,Website,,,,,,,,,,,,,website.com`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/securenotes.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/securenotes.csv.ts new file mode 100644 index 00000000..22a3b904 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/securenotes.csv.ts @@ -0,0 +1,2 @@ +export const secureNoteData = `title,note +01,test`; diff --git a/jslib/common/spec/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts b/jslib/common/spec/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts new file mode 100644 index 00000000..2f2c1b55 --- /dev/null +++ b/jslib/common/spec/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts @@ -0,0 +1,4 @@ +export const data = `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" +"chrome://FirefoxAccounts","bla-bla-foo-bar","{""version"":1,""accountData"":{""kSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kXCS"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtKbHash"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""scopedKeys"":{""https://identity.mozilla.com/apps/oldsync"":{""kid"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""},""sync:addon_storage"":{""kid"":""xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""}}}}","Firefox Accounts credentials",,"{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +`; diff --git a/jslib/common/spec/importers/testData/firefoxCsv/simplePasswordData.csv.ts b/jslib/common/spec/importers/testData/firefoxCsv/simplePasswordData.csv.ts new file mode 100644 index 00000000..90e19f19 --- /dev/null +++ b/jslib/common/spec/importers/testData/firefoxCsv/simplePasswordData.csv.ts @@ -0,0 +1,2 @@ +export const data = `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" +"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900"`; diff --git a/jslib/common/spec/importers/testData/keeperJson/testData.ts b/jslib/common/spec/importers/testData/keeperJson/testData.ts new file mode 100644 index 00000000..fee8f54c --- /dev/null +++ b/jslib/common/spec/importers/testData/keeperJson/testData.ts @@ -0,0 +1,90 @@ +import { KeeperJsonExport } from "jslib-common/importers/keeperImporters/types/keeperJsonTypes"; + +export const testData: KeeperJsonExport = { + shared_folders: [ + { + path: "My Customer 1", + manage_users: true, + manage_records: true, + can_edit: true, + can_share: true, + permissions: [ + { + uid: "kVM96KGEoGxhskZoSTd_jw", + manage_users: true, + manage_records: true, + }, + { + name: "user@mycompany.com", + manage_users: true, + manage_records: true, + }, + ], + }, + { + path: "Testing\\My Customer 2", + manage_users: true, + manage_records: true, + can_edit: true, + can_share: true, + permissions: [ + { + uid: "ih1CggiQ-3ENXcn4G0sl-g", + manage_users: true, + manage_records: true, + }, + { + name: "user@mycompany.com", + manage_users: true, + manage_records: true, + }, + ], + }, + ], + records: [ + { + title: "Bank Account 1", + login: "customer1234", + password: "4813fJDHF4239fdk", + login_url: "https://chase.com", + notes: "These are some notes.", + custom_fields: { + "Account Number": "123-456-789", + }, + folders: [ + { + folder: "Optional Private Folder 1", + }, + ], + }, + { + title: "Bank Account 2", + login: "mybankusername", + password: "w4k4k193f$^&@#*%2", + login_url: "https://amex.com", + notes: "Some great information here.", + custom_fields: { + "Security Group": "Public", + "IP Address": "12.45.67.8", + "TFC:Keeper": + "otpauth://totp/Amazon:me@company.com?secret=JBSWY3DPEHPK3PXP&issuer=Amazon&algorithm=SHA1&digits=6&period=30", + }, + folders: [ + { + folder: "Optional Private Folder 1", + }, + { + shared_folder: "My Customer 1", + can_edit: true, + can_share: true, + }, + ], + }, + { + title: "Some Account", + login: "someUserName", + password: "w4k4k1wergf$^&@#*%2", + login_url: "https://example.com", + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserAccount.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserAccount.csv.ts new file mode 100644 index 00000000..5ccf0971 --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserAccount.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userAccountData = `nickname,url,username,password,additionalInfo,twofaSecret,status,tags +PasswordNickname,www.google.com,user.name@email.com,abc123,This is the additional information text.,someTOTPSeed,active,someTag`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserCreditCard.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserCreditCard.csv.ts new file mode 100644 index 00000000..0b127627 --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserCreditCard.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userCreditCardData = `nickname,status,tags,cardNumber,cardName,exp_month,exp_year,cvv,additionalInfo +Visa test card,active,someTag,4111111111111111,Joe User,04,24,222,This is the additional information field`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserIdCard.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserIdCard.csv.ts new file mode 100644 index 00000000..8f3fa600 --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserIdCard.csv.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export const userIdCardData = `nickname,status,tags,idType,idNumber,idName,idIssuanceDate,idExpirationDate,idCountry,additionalInfo +Joe User's nickname,active,someTag,Driver's License,123456,Joe M User,02/02/2022,02/02/2024,United States,Additional information +Passport ID card,active,someTag,Passport,1234567,Joe M User,03/07/2022,03/07/2028,United States,Additional information field +Social Security ID card,active,someTag,Social Security,123455678,Joe M User,03/07/2022,03/07/2028,United States,Additional information field text +ID card type ID card,active,someTag,ID Card,1234566,Joe M User,03/07/2022,03/07/2028,United States,Additional Information field text +Tax number ID card,active,someTag,Tax Number,12345678,Joe M User,03/07/2022,03/07/2028,United States,Additinoal information text field +Bank account ID card,active,someTag,Bank Account,12344556677,Joe M User,03/07/2022,03/07/2028,United States,Additional text information here +Insurance card ID card,active,someTag,Insurance Card,123456677,Joe M User,03/07/2022,03/07/2022,United States,Additional information text goes here +Health card Id card,active,someTag,Health Card,1234670,Joe M User,03/07/2022,03/07/2028,United States,More info +Membership ID card,active,someTag,Membership,12345709,Joe M User,03/07/2022,03/07/2028,United States,Add'l info +Database ID card,active,someTag,Database,12345089u,Joe M User,03/07/2022,03/07/2028,United States,Addin't info +Outdoor license ID card,active,someTag,Outdoor License,123890090,Joe M User,03/07/2022,03/07/2028,United States,Additional info +Reward program Id card,active,someTag,Reward Program,12345890b,Joe M User,03/07/2022,03/07/2028,United States,1234890 +Software license ID card,active,someTag,Software License,1234567c,Joe M User,03/07/2022,03/07/2028,United States,"It seems like the fields don't change, which makes it pretty useless that they have so many ID card types." +Tour visa ID card,active,someTag,Tour Visa,123456lkhj,Joe M User,03/07/2022,03/07/2028,United States,Additional Informaion text`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserIdentity.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserIdentity.csv.ts new file mode 100644 index 00000000..13afb16d --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserIdentity.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userIdentityData = `nickname,status,tags,firstName,middleName,lastName,email,firstAddressLine,secondAddressLine,title,gender,number,city,country,zipCode,additionalInfo +Joe User's nickname,active,someTag,Joe,M,User,joe.user@email.com,1 Example House,Suite 300,Mr,Male,2223334444,Portland,United States,04101,Additional information field`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserNote.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserNote.csv.ts new file mode 100644 index 00000000..8094c92f --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserNote.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userNoteData = `nickname,status,content +The title of a secure note,active,"The content of a secure note. Lorem ipsum, etc."`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserTwofa.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserTwofa.csv.ts new file mode 100644 index 00000000..68cfd44f --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserTwofa.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userTwoFaData = `nickname,status,tags,authToken,additionalInfo +2FA nickname,active,someTag,someTOTPSeed,"Additional information field content. "`; diff --git a/jslib/common/spec/importers/testData/nordpassCsv/nordpass.card.csv.ts b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.card.csv.ts new file mode 100644 index 00000000..8d79d2b1 --- /dev/null +++ b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.card.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password,note,cardholdername,cardnumber,cvc,expirydate,zipcode,folder,full_name,phone_number,email,address1,address2,city,country,state +SomeVisa,,,,,SomeHolder,4024007103939509,123,01 / 22,12345,,,,,,,,,`; diff --git a/jslib/common/spec/importers/testData/nordpassCsv/nordpass.identity.csv.ts b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.identity.csv.ts new file mode 100644 index 00000000..4dde389c --- /dev/null +++ b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.identity.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password,note,cardholdername,cardnumber,cvc,expirydate,zipcode,folder,full_name,phone_number,email,address1,address2,city,country,state +SomeTitle,,,,SomeNoteToMyIdentity,,,,,123456,,#fullName,123456789,hello@bitwarden.com,Test street 123,additional addressinfo,Cologne,Germany,North-Rhine-Westphalia`; diff --git a/jslib/common/spec/importers/testData/nordpassCsv/nordpass.login.csv.ts b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.login.csv.ts new file mode 100644 index 00000000..e2af76b0 --- /dev/null +++ b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.login.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password,note,cardholdername,cardnumber,cvc,expirydate,zipcode,folder,full_name,phone_number,email,address1,address2,city,country,state +SomeVaultItemName,https://example.com,hello@bitwarden.com,someStrongPassword,Some note for the VaultItem,,,,,,SomeFolderForVaultItem,,,,,,,,`; diff --git a/jslib/common/spec/importers/testData/nordpassCsv/nordpass.secureNote.csv.ts b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.secureNote.csv.ts new file mode 100644 index 00000000..0e4dcb2e --- /dev/null +++ b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.secureNote.csv.ts @@ -0,0 +1,3 @@ +export const data = `name,url,username,password,note,cardholdername,cardnumber,cvc,expirydate,zipcode,folder,full_name,phone_number,email,address1,address2,city,country,state +notesFolder,,,,,,,,,,,,,,,,,, +MySuperSecureNoteTitle,,,,MySuperSecureNote,,,,,,notesFolder,,,,,,,,`; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/APICredentials.ts b/jslib/common/spec/importers/testData/onePassword1Pux/APICredentials.ts new file mode 100644 index 00000000..41b75331 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/APICredentials.ts @@ -0,0 +1,169 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const APICredentialsData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "6nqnjdqyk5mwvqbdgbdr47oabe", + favIndex: 0, + createdAt: 1619465969, + updatedAt: 1619466052, + trashed: false, + categoryUuid: "112", + details: { + loginFields: [], + notesPlain: "My API Credential", + sections: [ + { + title: "", + fields: [ + { + title: "username", + id: "username", + value: { + string: "apiuser@nullvalue.test", + }, + indexAtSource: 0, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "credential", + id: "credential", + value: { + concealed: "apiapiapiapiapiapiappy", + }, + indexAtSource: 1, + guarded: true, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "default", + }, + }, + { + title: "type", + id: "type", + value: { + menu: "jwt", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "filename", + id: "filename", + value: { + string: "filename.jwt", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "valid from", + id: "validFrom", + value: { + date: 1301918460, + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expires", + id: "expires", + value: { + date: 1932811260, + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "hostname", + id: "hostname", + value: { + string: "not.your.everyday.hostname", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "", + title: "API Credential", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/BankAccount.ts b/jslib/common/spec/importers/testData/onePassword1Pux/BankAccount.ts new file mode 100644 index 00000000..1ada2714 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/BankAccount.ts @@ -0,0 +1,224 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const BankAccountData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "u2l4sjbencvsowwjuj3dfpt73q", + favIndex: 0, + createdAt: 1619466056, + updatedAt: 1619466187, + trashed: false, + categoryUuid: "101", + details: { + loginFields: [], + notesPlain: "My Bank Account", + sections: [ + { + title: "", + fields: [ + { + title: "bank name", + id: "bankName", + value: { + string: "Super Credit Union", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "name on account", + id: "owner", + value: { + string: "Cool Guy", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "type", + id: "accountType", + value: { + menu: "checking", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "routing number", + id: "routingNo", + value: { + string: "111000999", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "account number", + id: "accountNo", + value: { + string: "192837465918273645", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "SWIFT", + id: "swift", + value: { + string: "123456", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "IBAN", + id: "iban", + value: { + string: "DE12 123456", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "PIN", + id: "telephonePin", + value: { + concealed: "5555", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Branch Information", + name: "branchInfo", + fields: [ + { + title: "phone", + id: "branchPhone", + value: { + phone: "9399399933", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "address", + id: "branchAddress", + value: { + string: "1 Fifth Avenue", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "sentences", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "Super Credit Union", + tags: ["Finance"], + title: "Bank Account", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/CreditCard.ts b/jslib/common/spec/importers/testData/onePassword1Pux/CreditCard.ts new file mode 100644 index 00000000..ccdd4aa9 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/CreditCard.ts @@ -0,0 +1,343 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const CreditCardData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "vpxi2esuujz7nrbojp34rd5aja", + favIndex: 0, + createdAt: 1619465282, + updatedAt: 1619465447, + trashed: false, + categoryUuid: "002", + details: { + loginFields: [], + notesPlain: "My parents' credit card. ", + sections: [ + { + title: "", + fields: [ + { + title: "cardholder name", + id: "cardholder", + value: { + string: "Fred Engels", + }, + indexAtSource: 0, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "type", + id: "type", + value: { + creditCardType: "discover", + }, + indexAtSource: 1, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "number", + id: "ccnum", + value: { + creditCardNumber: "6011111111111117", + }, + indexAtSource: 2, + guarded: true, + clipboardFilter: "0123456789", + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "verification number", + id: "cvv", + value: { + concealed: "1312", + }, + indexAtSource: 3, + guarded: true, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expiry date", + id: "expiry", + value: { + monthYear: 209912, + }, + indexAtSource: 4, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "valid from", + id: "validFrom", + value: { + monthYear: 200101, + }, + indexAtSource: 5, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "", + id: "txbzvwzpck7ejhfres3733rbpm", + value: { + string: "card", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Contact Information", + name: "contactInfo", + fields: [ + { + title: "issuing bank", + id: "bank", + value: { + string: "Some bank", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "phone (local)", + id: "phoneLocal", + value: { + phone: "123456", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "phone (toll free)", + id: "phoneTollFree", + value: { + phone: "0800123456", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "phone (intl)", + id: "phoneIntl", + value: { + phone: "+49123456", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "website", + id: "website", + value: { + url: "somebank.com", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Additional Details", + name: "details", + fields: [ + { + title: "PIN", + id: "pin", + value: { + concealed: "1234", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "credit limit", + id: "creditLimit", + value: { + string: "$1312", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "cash withdrawal limit", + id: "cashLimit", + value: { + string: "$500", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "interest rate", + id: "interest", + value: { + string: "1%", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "issue number", + id: "issuenumber", + value: { + string: "123456", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "1234 **** 6789", + tags: ["Finance"], + title: "Parent's Credit Card", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/Database.ts b/jslib/common/spec/importers/testData/onePassword1Pux/Database.ts new file mode 100644 index 00000000..594f5384 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/Database.ts @@ -0,0 +1,201 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const DatabaseData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "ospvepl3ex2y6hjwwqwyvtf2sy", + favIndex: 0, + createdAt: 1619466193, + updatedAt: 1619466276, + trashed: false, + categoryUuid: "102", + details: { + loginFields: [], + notesPlain: "My Database", + sections: [ + { + title: "", + fields: [ + { + title: "type", + id: "database_type", + value: { + menu: "postgresql", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "server", + id: "hostname", + value: { + string: "my.secret.db.server", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + { + title: "port", + id: "port", + value: { + string: "1337", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "database", + id: "database", + value: { + string: "user_database", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "username", + id: "username", + value: { + string: "cooldbuser", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "password", + id: "password", + value: { + concealed: "^+kTjhLaN7wVPAhGU)*J", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "SID", + id: "sid", + value: { + string: "ASDIUFU-283234", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "alias", + id: "alias", + value: { + string: "cdbu", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "connection options", + id: "options", + value: { + string: "ssh", + }, + indexAtSource: 8, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "my.secret.db.server", + title: "Database", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/DriversLicense.ts b/jslib/common/spec/importers/testData/onePassword1Pux/DriversLicense.ts new file mode 100644 index 00000000..468069ae --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/DriversLicense.ts @@ -0,0 +1,233 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const DriversLicenseData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "nntuge2g7s2wrlokyfhea354ay", + favIndex: 0, + createdAt: 1619466279, + updatedAt: 1619466425, + trashed: false, + categoryUuid: "103", + details: { + loginFields: [], + notesPlain: "My Driver's License", + sections: [ + { + title: "", + fields: [ + { + title: "full name", + id: "fullname", + value: { + string: "Michael Scarn", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "address", + id: "address", + value: { + string: "2120 Mifflin Rd.", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "sentences", + }, + }, + { + title: "date of birth", + id: "birthdate", + value: { + date: 252504060, + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "sex", + id: "sex", + value: { + gender: "male", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "height", + id: "height", + value: { + string: "5'11\"", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "number", + id: "number", + value: { + string: "12345678901", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "license class", + id: "class", + value: { + string: "C", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "conditions / restrictions", + id: "conditions", + value: { + string: "B", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "state", + id: "state", + value: { + string: "Pennsylvania", + }, + indexAtSource: 8, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "country", + id: "country", + value: { + string: "United States", + }, + indexAtSource: 9, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expiry date", + id: "expiry_date", + value: { + monthYear: 203012, + }, + indexAtSource: 10, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "12345678901", + title: "Michael Scarn", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/EmailAccount.ts b/jslib/common/spec/importers/testData/onePassword1Pux/EmailAccount.ts new file mode 100644 index 00000000..1f83a6d8 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/EmailAccount.ts @@ -0,0 +1,341 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const EmailAccountData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "p3hohdgwpt4u2ra2fc3tvzomsm", + favIndex: 0, + createdAt: 1619466428, + updatedAt: 1619466585, + trashed: false, + categoryUuid: "111", + details: { + loginFields: [], + notesPlain: "My Email Config", + sections: [ + { + title: "", + fields: [ + { + title: "type", + id: "pop_type", + value: { + menu: "either", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "username", + id: "pop_username", + value: { + string: "someuser@nullvalue.test", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "server", + id: "pop_server", + value: { + string: "mailserver.nullvalue.test", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + { + title: "port number", + id: "pop_port", + value: { + string: "587", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "password", + id: "pop_password", + value: { + concealed: "u1jsf + + + + + + + + + + + + This is a sample note. + 3 + + + + 5555123456789000 + John Smith + 01/23 + 555 + 1111 + 555-0153 + 1 + + + john555@gmail.com + early91*Fail* + https://www.facebook.com + 3 + + + john555@gmail.com + plain79{Area{ + https://www.google.com + 3 + thisisanotp + thisshouldbehidden + + + 555111111 + John Smith + 05/05/1980 + 01/01/2018 + 01/01/2028 + 3 + This is a note attached to a card + + + john555@gmail.com + https://twitter.com + 3 + shouldbepassword + + + john555 + Save63\apple\ + 3 + + +`; diff --git a/jslib/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts b/jslib/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts new file mode 100644 index 00000000..2f4fe6c3 --- /dev/null +++ b/jslib/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts @@ -0,0 +1,114 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { ApiLogInStrategy } from "jslib-common/misc/logInStrategies/apiLogin.strategy"; +import { Utils } from "jslib-common/misc/utils"; +import { ApiLogInCredentials } from "jslib-common/models/domain/logInCredentials"; + +import { identityTokenResponseFactory } from "./logIn.strategy.spec"; + +describe("ApiLogInStrategy", () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let logService: SubstituteOf; + let environmentService: SubstituteOf; + let keyConnectorService: SubstituteOf; + let stateService: SubstituteOf; + let twoFactorService: SubstituteOf; + + let apiLogInStrategy: ApiLogInStrategy; + let credentials: ApiLogInCredentials; + + const deviceId = Utils.newGuid(); + const keyConnectorUrl = "KEY_CONNECTOR_URL"; + const apiClientId = "API_CLIENT_ID"; + const apiClientSecret = "API_CLIENT_SECRET"; + + beforeEach(async () => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + logService = Substitute.for(); + environmentService = Substitute.for(); + stateService = Substitute.for(); + keyConnectorService = Substitute.for(); + twoFactorService = Substitute.for(); + + appIdService.getAppId().resolves(deviceId); + tokenService.getTwoFactorToken().resolves(null); + + apiLogInStrategy = new ApiLogInStrategy( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService, + environmentService, + keyConnectorService + ); + + credentials = new ApiLogInCredentials(apiClientId, apiClientSecret); + }); + + it("sends api key credentials to the server", async () => { + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + await apiLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const apiTokenRequest = actual as any; + return ( + apiTokenRequest.clientId === apiClientId && + apiTokenRequest.clientSecret === apiClientSecret && + apiTokenRequest.device.identifier === deviceId && + apiTokenRequest.twoFactor.provider == null && + apiTokenRequest.twoFactor.token == null && + apiTokenRequest.captchaResponse == null + ); + }) + ); + }); + + it("sets the local environment after a successful login", async () => { + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + + await apiLogInStrategy.logIn(credentials); + + stateService.received(1).setApiKeyClientId(apiClientId); + stateService.received(1).setApiKeyClientSecret(apiClientSecret); + stateService.received(1).addAccount(Arg.any()); + }); + + it("gets and sets the Key Connector key from environmentUrl", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.apiUseKeyConnector = true; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + environmentService.getKeyConnectorUrl().returns(keyConnectorUrl); + + await apiLogInStrategy.logIn(credentials); + + keyConnectorService.received(1).getAndSetKey(keyConnectorUrl); + }); +}); diff --git a/jslib/common/spec/misc/logInStrategies/logIn.strategy.spec.ts b/jslib/common/spec/misc/logInStrategies/logIn.strategy.spec.ts new file mode 100644 index 00000000..5f68881b --- /dev/null +++ b/jslib/common/spec/misc/logInStrategies/logIn.strategy.spec.ts @@ -0,0 +1,288 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { AuthService } from "jslib-common/abstractions/auth.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType"; +import { PasswordLogInStrategy } from "jslib-common/misc/logInStrategies/passwordLogin.strategy"; +import { Utils } from "jslib-common/misc/utils"; +import { Account, AccountProfile, AccountTokens } from "jslib-common/models/domain/account"; +import { AuthResult } from "jslib-common/models/domain/authResult"; +import { EncString } from "jslib-common/models/domain/encString"; +import { PasswordLogInCredentials } from "jslib-common/models/domain/logInCredentials"; +import { PasswordTokenRequest } from "jslib-common/models/request/identityToken/passwordTokenRequest"; +import { TokenRequestTwoFactor } from "jslib-common/models/request/identityToken/tokenRequestTwoFactor"; +import { IdentityCaptchaResponse } from "jslib-common/models/response/identityCaptchaResponse"; +import { IdentityTokenResponse } from "jslib-common/models/response/identityTokenResponse"; +import { IdentityTwoFactorResponse } from "jslib-common/models/response/identityTwoFactorResponse"; + +const email = "hello@world.com"; +const masterPassword = "password"; + +const deviceId = Utils.newGuid(); +const accessToken = "ACCESS_TOKEN"; +const refreshToken = "REFRESH_TOKEN"; +const encKey = "ENC_KEY"; +const privateKey = "PRIVATE_KEY"; +const captchaSiteKey = "CAPTCHA_SITE_KEY"; +const kdf = 0; +const kdfIterations = 10000; +const userId = Utils.newGuid(); +const masterPasswordHash = "MASTER_PASSWORD_HASH"; + +const decodedToken = { + sub: userId, + email: email, + premium: false, +}; + +const twoFactorProviderType = TwoFactorProviderType.Authenticator; +const twoFactorToken = "TWO_FACTOR_TOKEN"; +const twoFactorRemember = true; + +export function identityTokenResponseFactory() { + return new IdentityTokenResponse({ + ForcePasswordReset: false, + Kdf: kdf, + KdfIterations: kdfIterations, + Key: encKey, + PrivateKey: privateKey, + ResetMasterPassword: false, + access_token: accessToken, + expires_in: 3600, + refresh_token: refreshToken, + scope: "api offline_access", + token_type: "Bearer", + }); +} + +describe("LogInStrategy", () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let logService: SubstituteOf; + let stateService: SubstituteOf; + let twoFactorService: SubstituteOf; + let authService: SubstituteOf; + + let passwordLogInStrategy: PasswordLogInStrategy; + let credentials: PasswordLogInCredentials; + + beforeEach(async () => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + logService = Substitute.for(); + stateService = Substitute.for(); + twoFactorService = Substitute.for(); + authService = Substitute.for(); + + appIdService.getAppId().resolves(deviceId); + + // The base class is abstract so we test it via PasswordLogInStrategy + passwordLogInStrategy = new PasswordLogInStrategy( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService, + authService + ); + credentials = new PasswordLogInCredentials(email, masterPassword); + }); + + describe("base class", () => { + it("sets the local environment after a successful login", async () => { + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + tokenService.decodeToken(accessToken).resolves(decodedToken); + + await passwordLogInStrategy.logIn(credentials); + + stateService.received(1).addAccount( + new Account({ + profile: { + ...new AccountProfile(), + ...{ + userId: userId, + email: email, + hasPremiumPersonally: false, + kdfIterations: kdfIterations, + kdfType: kdf, + }, + }, + tokens: { + ...new AccountTokens(), + ...{ + accessToken: accessToken, + refreshToken: refreshToken, + }, + }, + }) + ); + cryptoService.received(1).setEncKey(encKey); + cryptoService.received(1).setEncPrivateKey(privateKey); + + stateService.received(1).setBiometricLocked(false); + messagingService.received(1).send("loggedIn"); + }); + + it("builds AuthResult", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.forcePasswordReset = true; + tokenResponse.resetMasterPassword = true; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + const result = await passwordLogInStrategy.logIn(credentials); + + const expected = new AuthResult(); + expected.forcePasswordReset = true; + expected.resetMasterPassword = true; + expected.twoFactorProviders = null; + expected.captchaSiteKey = ""; + expect(result).toEqual(expected); + }); + + it("rejects login if CAPTCHA is required", async () => { + // Sample CAPTCHA response + const tokenResponse = new IdentityCaptchaResponse({ + error: "invalid_grant", + error_description: "Captcha required.", + HCaptcha_SiteKey: captchaSiteKey, + }); + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + const result = await passwordLogInStrategy.logIn(credentials); + + stateService.didNotReceive().addAccount(Arg.any()); + messagingService.didNotReceive().send(Arg.any()); + + const expected = new AuthResult(); + expected.captchaSiteKey = captchaSiteKey; + expect(result).toEqual(expected); + }); + + it("makes a new public and private key for an old account", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.privateKey = null; + cryptoService.makeKeyPair(Arg.any()).resolves(["PUBLIC_KEY", new EncString("PRIVATE_KEY")]); + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + await passwordLogInStrategy.logIn(credentials); + + apiService.received(1).postAccountKeys(Arg.any()); + }); + }); + + describe("Two-factor authentication", () => { + it("rejects login if 2FA is required", async () => { + // Sample response where TOTP 2FA required + const tokenResponse = new IdentityTwoFactorResponse({ + TwoFactorProviders: ["0"], + TwoFactorProviders2: { 0: null }, + error: "invalid_grant", + error_description: "Two factor required.", + }); + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + const result = await passwordLogInStrategy.logIn(credentials); + + stateService.didNotReceive().addAccount(Arg.any()); + messagingService.didNotReceive().send(Arg.any()); + + const expected = new AuthResult(); + expected.twoFactorProviders = new Map(); + expected.twoFactorProviders.set(0, null); + expect(result).toEqual(expected); + }); + + it("sends stored 2FA token to server", async () => { + tokenService.getTwoFactorToken().resolves(twoFactorToken); + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + + await passwordLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const passwordTokenRequest = actual as any; + return ( + passwordTokenRequest.twoFactor.provider === TwoFactorProviderType.Remember && + passwordTokenRequest.twoFactor.token === twoFactorToken && + passwordTokenRequest.twoFactor.remember === false + ); + }) + ); + }); + + it("sends 2FA token provided by user to server (single step)", async () => { + // This occurs if the user enters the 2FA code as an argument in the CLI + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + credentials.twoFactor = new TokenRequestTwoFactor( + twoFactorProviderType, + twoFactorToken, + twoFactorRemember + ); + + await passwordLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const passwordTokenRequest = actual as any; + return ( + passwordTokenRequest.twoFactor.provider === twoFactorProviderType && + passwordTokenRequest.twoFactor.token === twoFactorToken && + passwordTokenRequest.twoFactor.remember === twoFactorRemember + ); + }) + ); + }); + + it("sends 2FA token provided by user to server (two-step)", async () => { + // Simulate a partially completed login + passwordLogInStrategy.tokenRequest = new PasswordTokenRequest( + email, + masterPasswordHash, + null, + null + ); + + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + + await passwordLogInStrategy.logInTwoFactor( + new TokenRequestTwoFactor(twoFactorProviderType, twoFactorToken, twoFactorRemember), + null + ); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const passwordTokenRequest = actual as any; + return ( + passwordTokenRequest.twoFactor.provider === twoFactorProviderType && + passwordTokenRequest.twoFactor.token === twoFactorToken && + passwordTokenRequest.twoFactor.remember === twoFactorRemember + ); + }) + ); + }); + }); +}); diff --git a/jslib/common/spec/misc/logInStrategies/passwordLogIn.strategy.spec.ts b/jslib/common/spec/misc/logInStrategies/passwordLogIn.strategy.spec.ts new file mode 100644 index 00000000..65bf5367 --- /dev/null +++ b/jslib/common/spec/misc/logInStrategies/passwordLogIn.strategy.spec.ts @@ -0,0 +1,110 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { AuthService } from "jslib-common/abstractions/auth.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { HashPurpose } from "jslib-common/enums/hashPurpose"; +import { PasswordLogInStrategy } from "jslib-common/misc/logInStrategies/passwordLogin.strategy"; +import { Utils } from "jslib-common/misc/utils"; +import { PasswordLogInCredentials } from "jslib-common/models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; + +import { identityTokenResponseFactory } from "./logIn.strategy.spec"; + +const email = "hello@world.com"; +const masterPassword = "password"; +const hashedPassword = "HASHED_PASSWORD"; +const localHashedPassword = "LOCAL_HASHED_PASSWORD"; +const preloginKey = new SymmetricCryptoKey( + Utils.fromB64ToArray( + "N2KWjlLpfi5uHjv+YcfUKIpZ1l+W+6HRensmIqD+BFYBf6N/dvFpJfWwYnVBdgFCK2tJTAIMLhqzIQQEUmGFgg==" + ) +); +const deviceId = Utils.newGuid(); + +describe("PasswordLogInStrategy", () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let logService: SubstituteOf; + let stateService: SubstituteOf; + let twoFactorService: SubstituteOf; + let authService: SubstituteOf; + + let passwordLogInStrategy: PasswordLogInStrategy; + let credentials: PasswordLogInCredentials; + + beforeEach(async () => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + logService = Substitute.for(); + stateService = Substitute.for(); + twoFactorService = Substitute.for(); + authService = Substitute.for(); + + appIdService.getAppId().resolves(deviceId); + tokenService.getTwoFactorToken().resolves(null); + + authService.makePreloginKey(Arg.any(), Arg.any()).resolves(preloginKey); + + cryptoService.hashPassword(masterPassword, Arg.any()).resolves(hashedPassword); + cryptoService + .hashPassword(masterPassword, Arg.any(), HashPurpose.LocalAuthorization) + .resolves(localHashedPassword); + + passwordLogInStrategy = new PasswordLogInStrategy( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService, + authService + ); + credentials = new PasswordLogInCredentials(email, masterPassword); + + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + }); + + it("sends master password credentials to the server", async () => { + await passwordLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const passwordTokenRequest = actual as any; // Need to access private fields + return ( + passwordTokenRequest.email === email && + passwordTokenRequest.masterPasswordHash === hashedPassword && + passwordTokenRequest.device.identifier === deviceId && + passwordTokenRequest.twoFactor.provider == null && + passwordTokenRequest.twoFactor.token == null && + passwordTokenRequest.captchaResponse == null + ); + }) + ); + }); + + it("sets the local environment after a successful login", async () => { + await passwordLogInStrategy.logIn(credentials); + + cryptoService.received(1).setKey(preloginKey); + cryptoService.received(1).setKeyHash(localHashedPassword); + }); +}); diff --git a/jslib/common/spec/misc/logInStrategies/ssoLogIn.strategy.spec.ts b/jslib/common/spec/misc/logInStrategies/ssoLogIn.strategy.spec.ts new file mode 100644 index 00000000..826a16a5 --- /dev/null +++ b/jslib/common/spec/misc/logInStrategies/ssoLogIn.strategy.spec.ts @@ -0,0 +1,127 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { SsoLogInStrategy } from "jslib-common/misc/logInStrategies/ssoLogin.strategy"; +import { Utils } from "jslib-common/misc/utils"; +import { SsoLogInCredentials } from "jslib-common/models/domain/logInCredentials"; + +import { identityTokenResponseFactory } from "./logIn.strategy.spec"; + +describe("SsoLogInStrategy", () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let logService: SubstituteOf; + let keyConnectorService: SubstituteOf; + let stateService: SubstituteOf; + let twoFactorService: SubstituteOf; + + let ssoLogInStrategy: SsoLogInStrategy; + let credentials: SsoLogInCredentials; + + const deviceId = Utils.newGuid(); + const encKey = "ENC_KEY"; + const privateKey = "PRIVATE_KEY"; + const keyConnectorUrl = "KEY_CONNECTOR_URL"; + + const ssoCode = "SSO_CODE"; + const ssoCodeVerifier = "SSO_CODE_VERIFIER"; + const ssoRedirectUrl = "SSO_REDIRECT_URL"; + const ssoOrgId = "SSO_ORG_ID"; + + beforeEach(async () => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + logService = Substitute.for(); + stateService = Substitute.for(); + keyConnectorService = Substitute.for(); + twoFactorService = Substitute.for(); + + tokenService.getTwoFactorToken().resolves(null); + appIdService.getAppId().resolves(deviceId); + + ssoLogInStrategy = new SsoLogInStrategy( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService, + keyConnectorService + ); + credentials = new SsoLogInCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId); + }); + + it("sends SSO information to server", async () => { + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + + await ssoLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const ssoTokenRequest = actual as any; + return ( + ssoTokenRequest.code === ssoCode && + ssoTokenRequest.codeVerifier === ssoCodeVerifier && + ssoTokenRequest.redirectUri === ssoRedirectUrl && + ssoTokenRequest.device.identifier === deviceId && + ssoTokenRequest.twoFactor.provider == null && + ssoTokenRequest.twoFactor.token == null + ); + }) + ); + }); + + it("does not set keys for new SSO user flow", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.key = null; + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + await ssoLogInStrategy.logIn(credentials); + + cryptoService.didNotReceive().setEncPrivateKey(privateKey); + cryptoService.didNotReceive().setEncKey(encKey); + }); + + it("gets and sets KeyConnector key for enrolled user", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.keyConnectorUrl = keyConnectorUrl; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + await ssoLogInStrategy.logIn(credentials); + + keyConnectorService.received(1).getAndSetKey(keyConnectorUrl); + }); + + it("converts new SSO user to Key Connector on first login", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.keyConnectorUrl = keyConnectorUrl; + tokenResponse.key = null; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + await ssoLogInStrategy.logIn(credentials); + + keyConnectorService.received(1).convertNewSsoUserToKeyConnector(tokenResponse, ssoOrgId); + }); +}); diff --git a/jslib/common/spec/misc/sequentialize.spec.ts b/jslib/common/spec/misc/sequentialize.spec.ts new file mode 100644 index 00000000..4165eca2 --- /dev/null +++ b/jslib/common/spec/misc/sequentialize.spec.ts @@ -0,0 +1,127 @@ +import { sequentialize } from "jslib-common/misc/sequentialize"; + +describe("sequentialize decorator", () => { + it("should call the function once", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.bar(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(1); + }); + + it("should call the function once for each instance of the object", async () => { + const foo = new Foo(); + const foo2 = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.bar(1)); + promises.push(foo2.bar(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(1); + expect(foo2.calls).toBe(1); + }); + + it("should call the function once with key function", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.baz(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(1); + }); + + it("should call the function again when already resolved", async () => { + const foo = new Foo(); + await foo.bar(1); + expect(foo.calls).toBe(1); + await foo.bar(1); + expect(foo.calls).toBe(2); + }); + + it("should call the function again when already resolved with a key function", async () => { + const foo = new Foo(); + await foo.baz(1); + expect(foo.calls).toBe(1); + await foo.baz(1); + expect(foo.calls).toBe(2); + }); + + it("should call the function for each argument", async () => { + const foo = new Foo(); + await Promise.all([foo.bar(1), foo.bar(1), foo.bar(2), foo.bar(2), foo.bar(3), foo.bar(3)]); + expect(foo.calls).toBe(3); + }); + + it("should call the function for each argument with key function", async () => { + const foo = new Foo(); + await Promise.all([foo.baz(1), foo.baz(1), foo.baz(2), foo.baz(2), foo.baz(3), foo.baz(3)]); + expect(foo.calls).toBe(3); + }); + + it("should return correct result for each call", async () => { + const foo = new Foo(); + const allRes: number[] = []; + + await Promise.all([ + foo.bar(1).then((res) => allRes.push(res)), + foo.bar(1).then((res) => allRes.push(res)), + foo.bar(2).then((res) => allRes.push(res)), + foo.bar(2).then((res) => allRes.push(res)), + foo.bar(3).then((res) => allRes.push(res)), + foo.bar(3).then((res) => allRes.push(res)), + ]); + expect(foo.calls).toBe(3); + expect(allRes.length).toBe(6); + allRes.sort(); + expect(allRes).toEqual([2, 2, 4, 4, 6, 6]); + }); + + it("should return correct result for each call with key function", async () => { + const foo = new Foo(); + const allRes: number[] = []; + + await Promise.all([ + foo.baz(1).then((res) => allRes.push(res)), + foo.baz(1).then((res) => allRes.push(res)), + foo.baz(2).then((res) => allRes.push(res)), + foo.baz(2).then((res) => allRes.push(res)), + foo.baz(3).then((res) => allRes.push(res)), + foo.baz(3).then((res) => allRes.push(res)), + ]); + expect(foo.calls).toBe(3); + expect(allRes.length).toBe(6); + allRes.sort(); + expect(allRes).toEqual([3, 3, 6, 6, 9, 9]); + }); +}); + +class Foo { + calls = 0; + + @sequentialize((args) => "bar" + args[0]) + bar(a: number): Promise { + this.calls++; + return new Promise((res) => { + setTimeout(() => { + res(a * 2); + }, Math.random() * 100); + }); + } + + @sequentialize((args) => "baz" + args[0]) + baz(a: number): Promise { + this.calls++; + return new Promise((res) => { + setTimeout(() => { + res(a * 3); + }, Math.random() * 100); + }); + } +} diff --git a/jslib/common/spec/misc/throttle.spec.ts b/jslib/common/spec/misc/throttle.spec.ts new file mode 100644 index 00000000..5a8cd27e --- /dev/null +++ b/jslib/common/spec/misc/throttle.spec.ts @@ -0,0 +1,110 @@ +import { sequentialize } from "jslib-common/misc/sequentialize"; +import { throttle } from "jslib-common/misc/throttle"; + +describe("throttle decorator", () => { + it("should call the function once at a time", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.bar(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(10); + }); + + it("should call the function once at a time for each object", async () => { + const foo = new Foo(); + const foo2 = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.bar(1)); + promises.push(foo2.bar(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(10); + expect(foo2.calls).toBe(10); + }); + + it("should call the function limit at a time", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.baz(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(10); + }); + + it("should call the function limit at a time for each object", async () => { + const foo = new Foo(); + const foo2 = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.baz(1)); + promises.push(foo2.baz(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(10); + expect(foo2.calls).toBe(10); + }); + + it("should work together with sequentialize", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.qux(Math.floor(i / 2) * 2)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(5); + }); +}); + +class Foo { + calls = 0; + inflight = 0; + + @throttle(1, () => "bar") + bar(a: number) { + this.calls++; + this.inflight++; + return new Promise((res) => { + setTimeout(() => { + expect(this.inflight).toBe(1); + this.inflight--; + res(a * 2); + }, Math.random() * 10); + }); + } + + @throttle(5, () => "baz") + baz(a: number) { + this.calls++; + this.inflight++; + return new Promise((res) => { + setTimeout(() => { + expect(this.inflight).toBeLessThanOrEqual(5); + this.inflight--; + res(a * 3); + }, Math.random() * 10); + }); + } + + @sequentialize((args) => "qux" + args[0]) + @throttle(1, () => "qux") + qux(a: number) { + this.calls++; + this.inflight++; + return new Promise((res) => { + setTimeout(() => { + expect(this.inflight).toBe(1); + this.inflight--; + res(a * 3); + }, Math.random() * 10); + }); + } +} diff --git a/jslib/common/spec/misc/utils.spec.ts b/jslib/common/spec/misc/utils.spec.ts new file mode 100644 index 00000000..eec41e4b --- /dev/null +++ b/jslib/common/spec/misc/utils.spec.ts @@ -0,0 +1,73 @@ +import { Utils } from "jslib-common/misc/utils"; + +describe("Utils Service", () => { + describe("getDomain", () => { + it("should fail for invalid urls", () => { + expect(Utils.getDomain(null)).toBeNull(); + expect(Utils.getDomain(undefined)).toBeNull(); + expect(Utils.getDomain(" ")).toBeNull(); + expect(Utils.getDomain('https://bit!:"_&ward.com')).toBeNull(); + expect(Utils.getDomain("bitwarden")).toBeNull(); + }); + + it("should fail for data urls", () => { + expect(Utils.getDomain("data:image/jpeg;base64,AAA")).toBeNull(); + }); + + it("should handle urls without protocol", () => { + expect(Utils.getDomain("bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getDomain("wrong://bitwarden.com")).toBe("bitwarden.com"); + }); + + it("should handle valid urls", () => { + expect(Utils.getDomain("https://bitwarden")).toBe("bitwarden"); + expect(Utils.getDomain("https://bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getDomain("http://bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getDomain("http://vault.bitwarden.com")).toBe("bitwarden.com"); + expect( + Utils.getDomain("https://user:password@bitwarden.com:8080/password/sites?and&query#hash") + ).toBe("bitwarden.com"); + expect(Utils.getDomain("https://bitwarden.unknown")).toBe("bitwarden.unknown"); + }); + + it("should support localhost and IP", () => { + expect(Utils.getDomain("https://localhost")).toBe("localhost"); + expect(Utils.getDomain("https://192.168.1.1")).toBe("192.168.1.1"); + }); + + it("should reject invalid hostnames", () => { + expect(Utils.getDomain("https://mywebsite.com$.mywebsite.com")).toBeNull(); + expect(Utils.getDomain("https://mywebsite.com!.mywebsite.com")).toBeNull(); + }); + }); + + describe("getHostname", () => { + it("should fail for invalid urls", () => { + expect(Utils.getHostname(null)).toBeNull(); + expect(Utils.getHostname(undefined)).toBeNull(); + expect(Utils.getHostname(" ")).toBeNull(); + expect(Utils.getHostname('https://bit!:"_&ward.com')).toBeNull(); + expect(Utils.getHostname("bitwarden")).toBeNull(); + }); + + it("should handle valid urls", () => { + expect(Utils.getHostname("bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getHostname("https://bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getHostname("http://bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getHostname("http://vault.bitwarden.com")).toBe("vault.bitwarden.com"); + }); + + it("should support localhost and IP", () => { + expect(Utils.getHostname("https://localhost")).toBe("localhost"); + expect(Utils.getHostname("https://192.168.1.1")).toBe("192.168.1.1"); + }); + }); + + describe("newGuid", () => { + it("should create a valid guid", () => { + const validGuid = + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + expect(Utils.newGuid()).toMatch(validGuid); + }); + }); +}); diff --git a/jslib/common/spec/services/cipher.service.spec.ts b/jslib/common/spec/services/cipher.service.spec.ts new file mode 100644 index 00000000..79b75787 --- /dev/null +++ b/jslib/common/spec/services/cipher.service.spec.ts @@ -0,0 +1,69 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { FileUploadService } from "jslib-common/abstractions/fileUpload.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { SearchService } from "jslib-common/abstractions/search.service"; +import { SettingsService } from "jslib-common/abstractions/settings.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { Utils } from "jslib-common/misc/utils"; +import { Cipher } from "jslib-common/models/domain/cipher"; +import { EncArrayBuffer } from "jslib-common/models/domain/encArrayBuffer"; +import { EncString } from "jslib-common/models/domain/encString"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { CipherService } from "jslib-common/services/cipher.service"; + +const ENCRYPTED_TEXT = "This data has been encrypted"; +const ENCRYPTED_BYTES = new EncArrayBuffer(Utils.fromUtf8ToArray(ENCRYPTED_TEXT).buffer); + +describe("Cipher Service", () => { + let cryptoService: SubstituteOf; + let stateService: SubstituteOf; + let settingsService: SubstituteOf; + let apiService: SubstituteOf; + let fileUploadService: SubstituteOf; + let i18nService: SubstituteOf; + let searchService: SubstituteOf; + let logService: SubstituteOf; + + let cipherService: CipherService; + + beforeEach(() => { + cryptoService = Substitute.for(); + stateService = Substitute.for(); + settingsService = Substitute.for(); + apiService = Substitute.for(); + fileUploadService = Substitute.for(); + i18nService = Substitute.for(); + searchService = Substitute.for(); + logService = Substitute.for(); + + cryptoService.encryptToBytes(Arg.any(), Arg.any()).resolves(ENCRYPTED_BYTES); + cryptoService.encrypt(Arg.any(), Arg.any()).resolves(new EncString(ENCRYPTED_TEXT)); + + cipherService = new CipherService( + cryptoService, + settingsService, + apiService, + fileUploadService, + i18nService, + () => searchService, + logService, + stateService + ); + }); + + it("attachments upload encrypted file contents", async () => { + const fileName = "filename"; + const fileData = new Uint8Array(10).buffer; + cryptoService.getOrgKey(Arg.any()).resolves(new SymmetricCryptoKey(new Uint8Array(32).buffer)); + + await cipherService.saveAttachmentRawWithServer(new Cipher(), fileName, fileData); + + fileUploadService + .received(1) + .uploadCipherAttachment(Arg.any(), Arg.any(), new EncString(ENCRYPTED_TEXT), ENCRYPTED_BYTES); + }); +}); diff --git a/jslib/common/spec/services/consoleLog.service.spec.ts b/jslib/common/spec/services/consoleLog.service.spec.ts new file mode 100644 index 00000000..3f3059f7 --- /dev/null +++ b/jslib/common/spec/services/consoleLog.service.spec.ts @@ -0,0 +1,102 @@ +import { ConsoleLogService } from "jslib-common/services/consoleLog.service"; + +const originalConsole = console; +let caughtMessage: any; + +declare let console: any; + +export function interceptConsole(interceptions: any): object { + console = { + log: function () { + // eslint-disable-next-line + interceptions.log = arguments; + }, + warn: function () { + // eslint-disable-next-line + interceptions.warn = arguments; + }, + error: function () { + // eslint-disable-next-line + interceptions.error = arguments; + }, + }; + return interceptions; +} + +export function restoreConsole() { + console = originalConsole; +} + +describe("ConsoleLogService", () => { + let logService: ConsoleLogService; + beforeEach(() => { + caughtMessage = {}; + interceptConsole(caughtMessage); + logService = new ConsoleLogService(true); + }); + + afterAll(() => { + restoreConsole(); + }); + + it("filters messages below the set threshold", () => { + logService = new ConsoleLogService(true, () => true); + logService.debug("debug"); + logService.info("info"); + logService.warning("warning"); + logService.error("error"); + + expect(caughtMessage).toEqual({}); + }); + it("only writes debug messages in dev mode", () => { + logService = new ConsoleLogService(false); + + logService.debug("debug message"); + expect(caughtMessage.log).toBeUndefined(); + }); + + it("writes debug/info messages to console.log", () => { + logService.debug("this is a debug message"); + expect(caughtMessage).toMatchObject({ + log: { "0": "this is a debug message" }, + }); + + logService.info("this is an info message"); + expect(caughtMessage).toMatchObject({ + log: { "0": "this is an info message" }, + }); + }); + it("writes warning messages to console.warn", () => { + logService.warning("this is a warning message"); + expect(caughtMessage).toMatchObject({ + warn: { 0: "this is a warning message" }, + }); + }); + it("writes error messages to console.error", () => { + logService.error("this is an error message"); + expect(caughtMessage).toMatchObject({ + error: { 0: "this is an error message" }, + }); + }); + + it("times with output to info", async () => { + logService.time(); + await new Promise((r) => setTimeout(r, 250)); + const duration = logService.timeEnd(); + expect(duration[0]).toBe(0); + expect(duration[1]).toBeGreaterThan(0); + expect(duration[1]).toBeLessThan(500 * 10e6); + + expect(caughtMessage).toEqual(expect.arrayContaining([])); + expect(caughtMessage.log.length).toBe(1); + expect(caughtMessage.log[0]).toEqual(expect.stringMatching(/^default: \d+\.?\d*ms$/)); + }); + + it("filters time output", async () => { + logService = new ConsoleLogService(true, () => true); + logService.time(); + logService.timeEnd(); + + expect(caughtMessage).toEqual({}); + }); +}); diff --git a/jslib/common/spec/services/export.service.spec.ts b/jslib/common/spec/services/export.service.spec.ts new file mode 100644 index 00000000..c96d9f0a --- /dev/null +++ b/jslib/common/spec/services/export.service.spec.ts @@ -0,0 +1,209 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { CipherService } from "jslib-common/abstractions/cipher.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { FolderService } from "jslib-common/abstractions/folder.service"; +import { CipherType } from "jslib-common/enums/cipherType"; +import { KdfType } from "jslib-common/enums/kdfType"; +import { Utils } from "jslib-common/misc/utils"; +import { Cipher } from "jslib-common/models/domain/cipher"; +import { EncString } from "jslib-common/models/domain/encString"; +import { Login } from "jslib-common/models/domain/login"; +import { CipherWithIds as CipherExport } from "jslib-common/models/export/cipherWithIds"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { LoginView } from "jslib-common/models/view/loginView"; +import { ExportService } from "jslib-common/services/export.service"; + +import { BuildTestObject, GetUniqueString } from "../utils"; + +const UserCipherViews = [ + generateCipherView(false), + generateCipherView(false), + generateCipherView(true), +]; + +const UserCipherDomains = [ + generateCipherDomain(false), + generateCipherDomain(false), + generateCipherDomain(true), +]; + +function generateCipherView(deleted: boolean) { + return BuildTestObject( + { + id: GetUniqueString("id"), + notes: GetUniqueString("notes"), + type: CipherType.Login, + login: BuildTestObject( + { + username: GetUniqueString("username"), + password: GetUniqueString("password"), + }, + LoginView + ), + collectionIds: null, + deletedDate: deleted ? new Date() : null, + }, + CipherView + ); +} + +function generateCipherDomain(deleted: boolean) { + return BuildTestObject( + { + id: GetUniqueString("id"), + notes: new EncString(GetUniqueString("notes")), + type: CipherType.Login, + login: BuildTestObject( + { + username: new EncString(GetUniqueString("username")), + password: new EncString(GetUniqueString("password")), + }, + Login + ), + collectionIds: null, + deletedDate: deleted ? new Date() : null, + }, + Cipher + ); +} + +function expectEqualCiphers(ciphers: CipherView[] | Cipher[], jsonResult: string) { + const actual = JSON.stringify(JSON.parse(jsonResult).items); + const items: CipherExport[] = []; + ciphers.forEach((c: CipherView | Cipher) => { + const item = new CipherExport(); + item.build(c); + items.push(item); + }); + + expect(actual).toEqual(JSON.stringify(items)); +} + +describe("ExportService", () => { + let exportService: ExportService; + let apiService: SubstituteOf; + let cryptoFunctionService: SubstituteOf; + let cipherService: SubstituteOf; + let folderService: SubstituteOf; + let cryptoService: SubstituteOf; + + beforeEach(() => { + apiService = Substitute.for(); + cryptoFunctionService = Substitute.for(); + cipherService = Substitute.for(); + folderService = Substitute.for(); + cryptoService = Substitute.for(); + + folderService.getAllDecrypted().resolves([]); + folderService.getAll().resolves([]); + + exportService = new ExportService( + folderService, + cipherService, + apiService, + cryptoService, + cryptoFunctionService + ); + }); + + it("exports unecrypted user ciphers", async () => { + cipherService.getAllDecrypted().resolves(UserCipherViews.slice(0, 1)); + + const actual = await exportService.getExport("json"); + + expectEqualCiphers(UserCipherViews.slice(0, 1), actual); + }); + + it("exports encrypted json user ciphers", async () => { + cipherService.getAll().resolves(UserCipherDomains.slice(0, 1)); + + const actual = await exportService.getExport("encrypted_json"); + + expectEqualCiphers(UserCipherDomains.slice(0, 1), actual); + }); + + it("does not unecrypted export trashed user items", async () => { + cipherService.getAllDecrypted().resolves(UserCipherViews); + + const actual = await exportService.getExport("json"); + + expectEqualCiphers(UserCipherViews.slice(0, 2), actual); + }); + + it("does not encrypted export trashed user items", async () => { + cipherService.getAll().resolves(UserCipherDomains); + + const actual = await exportService.getExport("encrypted_json"); + + expectEqualCiphers(UserCipherDomains.slice(0, 2), actual); + }); + + describe("password protected export", () => { + let exportString: string; + let exportObject: any; + let mac: SubstituteOf; + let data: SubstituteOf; + const password = "password"; + const salt = "salt"; + + describe("export json object", () => { + beforeEach(async () => { + mac = Substitute.for(); + data = Substitute.for(); + + mac.encryptedString.returns("mac"); + data.encryptedString.returns("encData"); + + jest.spyOn(Utils, "fromBufferToB64").mockReturnValue(salt); + cipherService.getAllDecrypted().resolves(UserCipherViews.slice(0, 1)); + + exportString = await exportService.getPasswordProtectedExport(password); + exportObject = JSON.parse(exportString); + }); + + it("specifies it is encrypted", () => { + expect(exportObject.encrypted).toBe(true); + }); + + it("specifies it's password protected", () => { + expect(exportObject.passwordProtected).toBe(true); + }); + + it("specifies salt", () => { + expect(exportObject.salt).toEqual("salt"); + }); + + it("specifies kdfIterations", () => { + expect(exportObject.kdfIterations).toEqual(100000); + }); + + it("has kdfType", () => { + expect(exportObject.kdfType).toEqual(KdfType.PBKDF2_SHA256); + }); + + it("has a mac property", async () => { + cryptoService.encrypt(Arg.any(), Arg.any()).resolves(mac); + exportString = await exportService.getPasswordProtectedExport(password); + exportObject = JSON.parse(exportString); + + expect(exportObject.encKeyValidation_DO_NOT_EDIT).toEqual(mac.encryptedString); + }); + + it("has data property", async () => { + cryptoService.encrypt(Arg.any(), Arg.any()).resolves(data); + exportString = await exportService.getPasswordProtectedExport(password); + exportObject = JSON.parse(exportString); + + expect(exportObject.data).toEqual(data.encryptedString); + }); + + it("encrypts the data property", async () => { + const unencrypted = await exportService.getExport(); + expect(exportObject.data).not.toEqual(unencrypted); + }); + }); + }); +}); diff --git a/jslib/common/spec/services/import.service.spec.ts b/jslib/common/spec/services/import.service.spec.ts new file mode 100644 index 00000000..dbf31cb2 --- /dev/null +++ b/jslib/common/spec/services/import.service.spec.ts @@ -0,0 +1,72 @@ +import Substitute, { SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { CipherService } from "jslib-common/abstractions/cipher.service"; +import { CollectionService } from "jslib-common/abstractions/collection.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { FolderService } from "jslib-common/abstractions/folder.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { BitwardenPasswordProtectedImporter } from "jslib-common/importers/bitwardenPasswordProtectedImporter"; +import { Importer } from "jslib-common/importers/importer"; +import { Utils } from "jslib-common/misc/utils"; +import { ImportService } from "jslib-common/services/import.service"; + +describe("ImportService", () => { + let importService: ImportService; + let cipherService: SubstituteOf; + let folderService: SubstituteOf; + let apiService: SubstituteOf; + let i18nService: SubstituteOf; + let collectionService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let cryptoService: SubstituteOf; + + beforeEach(() => { + cipherService = Substitute.for(); + folderService = Substitute.for(); + apiService = Substitute.for(); + i18nService = Substitute.for(); + collectionService = Substitute.for(); + platformUtilsService = Substitute.for(); + cryptoService = Substitute.for(); + + importService = new ImportService( + cipherService, + folderService, + apiService, + i18nService, + collectionService, + platformUtilsService, + cryptoService + ); + }); + + describe("getImporterInstance", () => { + describe("Get bitPasswordProtected importer", () => { + let importer: Importer; + const organizationId = Utils.newGuid(); + const password = Utils.newGuid(); + + beforeEach(() => { + importer = importService.getImporter( + "bitwardenpasswordprotected", + organizationId, + password + ); + }); + + it("returns an instance of BitwardenPasswordProtectedImporter", () => { + expect(importer).toBeInstanceOf(BitwardenPasswordProtectedImporter); + }); + + it("has the appropriate organization Id", () => { + expect(importer.organizationId).toEqual(organizationId); + }); + + it("has the appropriate password", () => { + expect(Object.entries(importer)).toEqual(expect.arrayContaining([["password", password]])); + }); + }); + }); +}); diff --git a/jslib/common/spec/services/stateMigration.service.ts b/jslib/common/spec/services/stateMigration.service.ts new file mode 100644 index 00000000..d325585a --- /dev/null +++ b/jslib/common/spec/services/stateMigration.service.ts @@ -0,0 +1,84 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StateVersion } from "jslib-common/enums/stateVersion"; +import { StateFactory } from "jslib-common/factories/stateFactory"; +import { Account } from "jslib-common/models/domain/account"; +import { GlobalState } from "jslib-common/models/domain/globalState"; +import { StateMigrationService } from "jslib-common/services/stateMigration.service"; + +const userId = "USER_ID"; + +describe("State Migration Service", () => { + let storageService: SubstituteOf; + let secureStorageService: SubstituteOf; + let stateFactory: SubstituteOf; + + let stateMigrationService: StateMigrationService; + + beforeEach(() => { + storageService = Substitute.for(); + secureStorageService = Substitute.for(); + stateFactory = Substitute.for(); + + stateMigrationService = new StateMigrationService( + storageService, + secureStorageService, + stateFactory + ); + }); + + describe("StateVersion 3 to 4 migration", async () => { + beforeEach(() => { + const globalVersion3: Partial = { + stateVersion: StateVersion.Three, + }; + + storageService.get("global", Arg.any()).resolves(globalVersion3); + storageService.get("authenticatedAccounts", Arg.any()).resolves([userId]); + }); + + it("clears everBeenUnlocked", async () => { + const accountVersion3: Account = { + profile: { + apiKeyClientId: null, + convertAccountToKeyConnector: null, + email: "EMAIL", + emailVerified: true, + everBeenUnlocked: true, + hasPremiumPersonally: false, + kdfIterations: 100000, + kdfType: 0, + keyHash: "KEY_HASH", + lastSync: "LAST_SYNC", + userId: userId, + usesKeyConnector: false, + forcePasswordReset: false, + }, + }; + + const expectedAccountVersion4: Account = { + profile: { + ...accountVersion3.profile, + }, + }; + delete expectedAccountVersion4.profile.everBeenUnlocked; + + storageService.get(userId, Arg.any()).resolves(accountVersion3); + + await stateMigrationService.migrate(); + + storageService.received(1).save(userId, expectedAccountVersion4, Arg.any()); + }); + + it("updates StateVersion number", async () => { + await stateMigrationService.migrate(); + + storageService.received(1).save( + "global", + Arg.is((globals: GlobalState) => globals.stateVersion === StateVersion.Four), + Arg.any() + ); + }); + }); +}); diff --git a/jslib/common/spec/test.ts b/jslib/common/spec/test.ts new file mode 100644 index 00000000..9d9906aa --- /dev/null +++ b/jslib/common/spec/test.ts @@ -0,0 +1,5 @@ +import { webcrypto } from "crypto"; + +Object.defineProperty(window, "crypto", { + value: webcrypto, +}); diff --git a/jslib/common/spec/utils.ts b/jslib/common/spec/utils.ts new file mode 100644 index 00000000..58963d0f --- /dev/null +++ b/jslib/common/spec/utils.ts @@ -0,0 +1,37 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { EncString } from "jslib-common/models/domain/encString"; + +function newGuid() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} + +export function GetUniqueString(prefix = "") { + return prefix + "_" + newGuid(); +} + +export function BuildTestObject( + def: Partial> | T, + constructor?: new () => T +): T { + return Object.assign(constructor === null ? {} : new constructor(), def) as T; +} + +export function mockEnc(s: string): EncString { + const mock = Substitute.for(); + mock.decrypt(Arg.any(), Arg.any()).resolves(s); + + return mock; +} + +export function makeStaticByteArray(length: number) { + const arr = new Uint8Array(length); + for (let i = 0; i < length; i++) { + arr[i] = i; + } + return arr; +} diff --git a/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts b/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts new file mode 100644 index 00000000..3e437c6a --- /dev/null +++ b/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts @@ -0,0 +1,558 @@ +import Substitute from "@fluffy-spoon/substitute"; + +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { Utils } from "jslib-common/misc/utils"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service"; + +const RsaPublicKey = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" + + "4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP" + + "RQ1H7iNHVZz3K8Db9GCSKPeC8MbW6gVCzb15esCe1gGzg6wkMuWYDFYPoh/oBqcIqrGah7firqB1nDedzEjw32heP2DAffVN" + + "084iTDjiWrJNUxBJ2pDD5Z9dT3MzQ2s09ew1yMWK2z37rT3YerC7OgEDmo3WYo3xL3qYJznu3EO2nmrYjiRa40wKSjxsTlUc" + + "xDF+F0uMW8oR9EMUHgepdepfAtLsSAQIDAQAB"; +const RsaPrivateKey = + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXRVrCX+2hfOQS8Hz" + + "YUS2oc/jGVTZpv+/Ryuoh9d8ihYX9dd0cYh2tl6KWdFc88lPUH11Oxqy20Rk2e5r/RF6T9yM0Me3NPnaKt+hlhLtfoc0h86L" + + "nhD56A9FDUfuI0dVnPcrwNv0YJIo94LwxtbqBULNvXl6wJ7WAbODrCQy5ZgMVg+iH+gGpwiqsZqHt+KuoHWcN53MSPDfaF4/" + + "YMB99U3TziJMOOJask1TEEnakMPln11PczNDazT17DXIxYrbPfutPdh6sLs6AQOajdZijfEvepgnOe7cQ7aeatiOJFrjTApK" + + "PGxOVRzEMX4XS4xbyhH0QxQeB6l16l8C0uxIBAgMBAAECggEASaWfeVDA3cVzOPFSpvJm20OTE+R6uGOU+7vh36TX/POq92q" + + "Buwbd0h0oMD32FxsXywd2IxtBDUSiFM9699qufTVuM0Q3tZw6lHDTOVG08+tPdr8qSbMtw7PGFxN79fHLBxejjO4IrM9lapj" + + "WpxEF+11x7r+wM+0xRZQ8sNFYG46aPfIaty4BGbL0I2DQ2y8I57iBCAy69eht59NLMm27fRWGJIWCuBIjlpfzET1j2HLXUIh" + + "5bTBNzqaN039WH49HczGE3mQKVEJZc/efk3HaVd0a1Sjzyn0QY+N1jtZN3jTRbuDWA1AknkX1LX/0tUhuS3/7C3ejHxjw4Dk" + + "1ZLo5/QKBgQDIWvqFn0+IKRSu6Ua2hDsufIHHUNLelbfLUMmFthxabcUn4zlvIscJO00Tq/ezopSRRvbGiqnxjv/mYxucvOU" + + "BeZtlus0Q9RTACBtw9TGoNTmQbEunJ2FOSlqbQxkBBAjgGEppRPt30iGj/VjAhCATq2MYOa/X4dVR51BqQAFIEwKBgQDBSIf" + + "TFKC/hDk6FKZlgwvupWYJyU9RkyfstPErZFmzoKhPkQ3YORo2oeAYmVUbS9I2iIYpYpYQJHX8jMuCbCz4ONxTCuSIXYQYUcU" + + "q4PglCKp31xBAE6TN8SvhfME9/MvuDssnQinAHuF0GDAhF646T3LLS1not6Vszv7brwSoGwKBgQC88v/8cGfi80ssQZeMnVv" + + "q1UTXIeQcQnoY5lGHJl3K8mbS3TnXE6c9j417Fdz+rj8KWzBzwWXQB5pSPflWcdZO886Xu/mVGmy9RWgLuVFhXwCwsVEPjNX" + + "5ramRb0/vY0yzenUCninBsIxFSbIfrPtLUYCc4hpxr+sr2Mg/y6jpvQKBgBezMRRs3xkcuXepuI2R+BCXL1/b02IJTUf1F+1" + + "eLLGd7YV0H+J3fgNc7gGWK51hOrF9JBZHBGeOUPlaukmPwiPdtQZpu4QNE3l37VlIpKTF30E6mb+BqR+nht3rUjarnMXgAoE" + + "Z18y6/KIjpSMpqC92Nnk/EBM9EYe6Cf4eA9ApAoGAeqEUg46UTlJySkBKURGpIs3v1kkf5I0X8DnOhwb+HPxNaiEdmO7ckm8" + + "+tPVgppLcG0+tMdLjigFQiDUQk2y3WjyxP5ZvXu7U96jaJRI8PFMoE06WeVYcdIzrID2HvqH+w0UQJFrLJ/0Mn4stFAEzXKZ" + + "BokBGnjFnTnKcs7nv/O8="; + +const Sha1Mac = "4d4c223f95dc577b665ec4ccbcb680b80a397038"; +const Sha256Mac = "6be3caa84922e12aaaaa2f16c40d44433bb081ef323db584eb616333ab4e874f"; +const Sha512Mac = + "21910e341fa12106ca35758a2285374509326c9fbe0bd64e7b99c898f841dc948c58ce66d3504d8883c" + + "5ea7817a0b7c5d4d9b00364ccd214669131fc17fe4aca"; + +describe("WebCrypto Function Service", () => { + describe("pbkdf2", () => { + const regular256Key = "pj9prw/OHPleXI6bRdmlaD+saJS4awrMiQsQiDjeu2I="; + const utf8256Key = "yqvoFXgMRmHR3QPYr5pyR4uVuoHkltv9aHUP63p8n7I="; + const unicode256Key = "ZdeOata6xoRpB4DLp8zHhXz5kLmkWtX5pd+TdRH8w8w="; + + const regular512Key = + "liTi/Ke8LPU1Qv+Vl7NGEVt/XMbsBVJ2kQxtVG/Z1/JFHFKQW3ZkI81qVlwTiCpb+cFXzs+57" + + "eyhhx5wfKo5Cg=="; + const utf8512Key = + "df0KdvIBeCzD/kyXptwQohaqUa4e7IyFUyhFQjXCANu5T+scq55hCcE4dG4T/MhAk2exw8j7ixRN" + + "zXANiVZpnw=="; + const unicode512Key = + "FE+AnUJaxv8jh+zUDtZz4mjjcYk0/PZDZm+SLJe3XtxtnpdqqpblX6JjuMZt/dYYNMOrb2+mD" + + "L3FiQDTROh1lg=="; + + testPbkdf2("sha256", regular256Key, utf8256Key, unicode256Key); + testPbkdf2("sha512", regular512Key, utf8512Key, unicode512Key); + }); + + describe("hkdf", () => { + const regular256Key = "qBUmEYtwTwwGPuw/z6bs/qYXXYNUlocFlyAuuANI8Pw="; + const utf8256Key = "6DfJwW1R3txgiZKkIFTvVAb7qVlG7lKcmJGJoxR2GBU="; + const unicode256Key = "gejGI82xthA+nKtKmIh82kjw+ttHr+ODsUoGdu5sf0A="; + + const regular512Key = "xe5cIG6ZfwGmb1FvsOedM0XKOm21myZkjL/eDeKIqqM="; + const utf8512Key = "XQMVBnxVEhlvjSFDQc77j5GDE9aorvbS0vKnjhRg0LY="; + const unicode512Key = "148GImrTbrjaGAe/iWEpclINM8Ehhko+9lB14+52lqc="; + + testHkdf("sha256", regular256Key, utf8256Key, unicode256Key); + testHkdf("sha512", regular512Key, utf8512Key, unicode512Key); + }); + + describe("hkdfExpand", () => { + const prk16Byte = "criAmKtfzxanbgea5/kelQ=="; + const prk32Byte = "F5h4KdYQnIVH4rKH0P9CZb1GrR4n16/sJrS0PsQEn0Y="; + const prk64Byte = + "ssBK0mRG17VHdtsgt8yo4v25CRNpauH+0r2fwY/E9rLyaFBAOMbIeTry+" + + "gUJ28p8y+hFh3EI9pcrEWaNvFYonQ=="; + + testHkdfExpand("sha256", prk32Byte, 32, "BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD8="); + testHkdfExpand( + "sha256", + prk32Byte, + 64, + "BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD9BV+" + + "/queOZenPNkDhmlVyL2WZ3OSU5+7ISNF5NhNfvZA==" + ); + testHkdfExpand("sha512", prk64Byte, 32, "uLWbMWodSBms5uGJ5WTRTesyW+MD7nlpCZvagvIRXlk="); + testHkdfExpand( + "sha512", + prk64Byte, + 64, + "uLWbMWodSBms5uGJ5WTRTesyW+MD7nlpCZvagvIRXlkY5Pv0sB+" + + "MqvaopmkC6sD/j89zDwTV9Ib2fpucUydO8w==" + ); + + it("should fail with prk too small", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const f = cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(prk16Byte), + "info", + 32, + "sha256" + ); + await expect(f).rejects.toEqual(new Error("prk is too small.")); + }); + + it("should fail with outputByteSize is too large", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const f = cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(prk32Byte), + "info", + 8161, + "sha256" + ); + await expect(f).rejects.toEqual(new Error("outputByteSize is too large.")); + }); + }); + + describe("hash", () => { + const regular1Hash = "2a241604fb921fad12bf877282457268e1dccb70"; + const utf81Hash = "85672798dc5831e96d6c48655d3d39365a9c88b6"; + const unicode1Hash = "39c975935054a3efc805a9709b60763a823a6ad4"; + + const regular256Hash = "2b8e96031d352a8655d733d7a930b5ffbea69dc25cf65c7bca7dd946278908b2"; + const utf8256Hash = "25fe8440f5b01ed113b0a0e38e721b126d2f3f77a67518c4a04fcde4e33eeb9d"; + const unicode256Hash = "adc1c0c2afd6e92cefdf703f9b6eb2c38e0d6d1a040c83f8505c561fea58852e"; + + const regular512Hash = + "c15cf11d43bde333647e3f559ec4193bb2edeaa0e8b902772f514cdf3f785a3f49a6e02a4b87b3" + + "b47523271ad45b7e0aebb5cdcc1bc54815d256eb5dcb80da9d"; + const utf8512Hash = + "035c31a877a291af09ed2d3a1a293e69c3e079ea2cecc00211f35e6bce10474ca3ad6e30b59e26118" + + "37463f20969c5bc95282965a051a88f8cdf2e166549fcdd"; + const unicode512Hash = + "2b16a5561af8ad6fe414cc103fc8036492e1fc6d9aabe1b655497054f760fe0e34c5d100ac773d" + + "9f3030438284f22dbfa20cb2e9b019f2c98dfe38ce1ef41bae"; + + const regularMd5 = "5eceffa53a5fd58c44134211e2c5f522"; + const utf8Md5 = "3abc9433c09551b939c80aa0aa3174e1"; + const unicodeMd5 = "85ae134072c8d81257933f7045ba17ca"; + + testHash("sha1", regular1Hash, utf81Hash, unicode1Hash); + testHash("sha256", regular256Hash, utf8256Hash, unicode256Hash); + testHash("sha512", regular512Hash, utf8512Hash, unicode512Hash); + testHash("md5", regularMd5, utf8Md5, unicodeMd5); + }); + + describe("hmac", () => { + testHmac("sha1", Sha1Mac); + testHmac("sha256", Sha256Mac); + testHmac("sha512", Sha512Mac); + }); + + describe("compare", () => { + it("should successfully compare two of the same values", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const equal = await cryptoFunctionService.compare(a.buffer, a.buffer); + expect(equal).toBe(true); + }); + + it("should successfully compare two different values of the same length", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const b = new Uint8Array(2); + b[0] = 3; + b[1] = 4; + const equal = await cryptoFunctionService.compare(a.buffer, b.buffer); + expect(equal).toBe(false); + }); + + it("should successfully compare two different values of different lengths", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const b = new Uint8Array(2); + b[0] = 3; + const equal = await cryptoFunctionService.compare(a.buffer, b.buffer); + expect(equal).toBe(false); + }); + }); + + describe("hmacFast", () => { + testHmacFast("sha1", Sha1Mac); + testHmacFast("sha256", Sha256Mac); + testHmacFast("sha512", Sha512Mac); + }); + + describe("compareFast", () => { + it("should successfully compare two of the same values", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const aByteString = Utils.fromBufferToByteString(a.buffer); + const equal = await cryptoFunctionService.compareFast(aByteString, aByteString); + expect(equal).toBe(true); + }); + + it("should successfully compare two different values of the same length", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const aByteString = Utils.fromBufferToByteString(a.buffer); + const b = new Uint8Array(2); + b[0] = 3; + b[1] = 4; + const bByteString = Utils.fromBufferToByteString(b.buffer); + const equal = await cryptoFunctionService.compareFast(aByteString, bByteString); + expect(equal).toBe(false); + }); + + it("should successfully compare two different values of different lengths", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const aByteString = Utils.fromBufferToByteString(a.buffer); + const b = new Uint8Array(2); + b[0] = 3; + const bByteString = Utils.fromBufferToByteString(b.buffer); + const equal = await cryptoFunctionService.compareFast(aByteString, bByteString); + expect(equal).toBe(false); + }); + }); + + describe("aesEncrypt", () => { + it("should successfully encrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = Utils.fromUtf8ToArray("EncryptMe!"); + const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + expect(Utils.fromBufferToB64(encValue)).toBe("ByUF8vhyX4ddU9gcooznwA=="); + }); + + it("should successfully encrypt and then decrypt data fast", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const encData = Utils.fromBufferToB64(encValue); + const b64Iv = Utils.fromBufferToB64(iv.buffer); + const symKey = new SymmetricCryptoKey(key.buffer); + const params = cryptoFunctionService.aesDecryptFastParameters(encData, b64Iv, null, symKey); + const decValue = await cryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe(value); + }); + + it("should successfully encrypt and then decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const decValue = await cryptoFunctionService.aesDecrypt(encValue, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe("aesDecryptFast", () => { + it("should successfully decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer); + const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer); + const data = "ByUF8vhyX4ddU9gcooznwA=="; + const params = cryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey); + const decValue = await cryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe("EncryptMe!"); + }); + }); + + describe("aesDecrypt", () => { + it("should successfully decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = Utils.fromB64ToArray("ByUF8vhyX4ddU9gcooznwA=="); + const decValue = await cryptoFunctionService.aesDecrypt(data.buffer, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); + }); + }); + + describe("rsaEncrypt", () => { + it("should successfully encrypt and then decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const pubKey = Utils.fromB64ToArray(RsaPublicKey); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await cryptoFunctionService.rsaEncrypt(data.buffer, pubKey.buffer, "sha1"); + const decValue = await cryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, "sha1"); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe("rsaDecrypt", () => { + it("should successfully decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const data = Utils.fromB64ToArray( + "A1/p8BQzN9UrbdYxUY2Va5+kPLyfZXF9JsZrjeEXcaclsnHurdxVAJcnbEqYMP3UXV" + + "4YAS/mpf+Rxe6/X0WS1boQdA0MAHSgx95hIlAraZYpiMLLiJRKeo2u8YivCdTM9V5vuAEJwf9Tof/qFsFci3sApdbATkorCT" + + "zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D" + + "/5jcercUtK2o+XrzNrL4UQ7yLZcFz6Bfwb/j6ICYvqd/YJwXNE6dwlL57OfwJyCdw2rRYf0/qI00t9u8Iitw==" + ); + const decValue = await cryptoFunctionService.rsaDecrypt(data.buffer, privKey.buffer, "sha1"); + expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); + }); + }); + + describe("rsaExtractPublicKey", () => { + it("should successfully extract key", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const publicKey = await cryptoFunctionService.rsaExtractPublicKey(privKey.buffer); + expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey); + }); + }); + + describe("rsaGenerateKeyPair", () => { + testRsaGenerateKeyPair(1024); + testRsaGenerateKeyPair(2048); + + // Generating 4096 bit keys can be slow. Commenting it out to save CI. + // testRsaGenerateKeyPair(4096); + }); + + describe("randomBytes", () => { + it("should make a value of the correct length", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const randomData = await cryptoFunctionService.randomBytes(16); + expect(randomData.byteLength).toBe(16); + }); + + it("should not make the same value twice", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const randomData = await cryptoFunctionService.randomBytes(16); + const randomData2 = await cryptoFunctionService.randomBytes(16); + expect( + randomData.byteLength === randomData2.byteLength && randomData !== randomData2 + ).toBeTruthy(); + }); + }); +}); + +function testPbkdf2( + algorithm: "sha256" | "sha512", + regularKey: string, + utf8Key: string, + unicodeKey: string +) { + const regularEmail = "user@example.com"; + const utf8Email = "üser@example.com"; + + const regularPassword = "password"; + const utf8Password = "pǻssword"; + const unicodePassword = "😀password🙏"; + + it("should create valid " + algorithm + " key from regular input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(regularPassword, regularEmail, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); + + it("should create valid " + algorithm + " key from utf8 input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(utf8Password, utf8Email, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(utf8Key); + }); + + it("should create valid " + algorithm + " key from unicode input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(unicodePassword, regularEmail, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(unicodeKey); + }); + + it("should create valid " + algorithm + " key from array buffer input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2( + Utils.fromUtf8ToArray(regularPassword).buffer, + Utils.fromUtf8ToArray(regularEmail).buffer, + algorithm, + 5000 + ); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); +} + +function testHkdf( + algorithm: "sha256" | "sha512", + regularKey: string, + utf8Key: string, + unicodeKey: string +) { + const ikm = Utils.fromB64ToArray("criAmKtfzxanbgea5/kelQ=="); + + const regularSalt = "salt"; + const utf8Salt = "üser_salt"; + const unicodeSalt = "😀salt🙏"; + + const regularInfo = "info"; + const utf8Info = "üser_info"; + const unicodeInfo = "😀info🙏"; + + it("should create valid " + algorithm + " key from regular input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, regularSalt, regularInfo, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); + + it("should create valid " + algorithm + " key from utf8 input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, utf8Salt, utf8Info, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(utf8Key); + }); + + it("should create valid " + algorithm + " key from unicode input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, unicodeSalt, unicodeInfo, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(unicodeKey); + }); + + it("should create valid " + algorithm + " key from array buffer input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf( + ikm, + Utils.fromUtf8ToArray(regularSalt).buffer, + Utils.fromUtf8ToArray(regularInfo).buffer, + 32, + algorithm + ); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); +} + +function testHkdfExpand( + algorithm: "sha256" | "sha512", + b64prk: string, + outputByteSize: number, + b64ExpectedOkm: string +) { + const info = "info"; + + it("should create valid " + algorithm + " " + outputByteSize + " byte okm", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const okm = await cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(b64prk), + info, + outputByteSize, + algorithm + ); + expect(Utils.fromBufferToB64(okm)).toBe(b64ExpectedOkm); + }); +} + +function testHash( + algorithm: "sha1" | "sha256" | "sha512" | "md5", + regularHash: string, + utf8Hash: string, + unicodeHash: string +) { + const regularValue = "HashMe!!"; + const utf8Value = "HǻshMe!!"; + const unicodeValue = "😀HashMe!!!🙏"; + + it("should create valid " + algorithm + " hash from regular input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(regularValue, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(regularHash); + }); + + it("should create valid " + algorithm + " hash from utf8 input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(utf8Value, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(utf8Hash); + }); + + it("should create valid " + algorithm + " hash from unicode input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(unicodeValue, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(unicodeHash); + }); + + it("should create valid " + algorithm + " hash from array buffer input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const hash = await cryptoFunctionService.hash( + Utils.fromUtf8ToArray(regularValue).buffer, + algorithm + ); + expect(Utils.fromBufferToHex(hash)).toBe(regularHash); + }); +} + +function testHmac(algorithm: "sha1" | "sha256" | "sha512", mac: string) { + it("should create valid " + algorithm + " hmac", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const computedMac = await cryptoFunctionService.hmac( + Utils.fromUtf8ToArray("SignMe!!").buffer, + Utils.fromUtf8ToArray("secretkey").buffer, + algorithm + ); + expect(Utils.fromBufferToHex(computedMac)).toBe(mac); + }); +} + +function testHmacFast(algorithm: "sha1" | "sha256" | "sha512", mac: string) { + it("should create valid " + algorithm + " hmac", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const keyByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("secretkey").buffer); + const dataByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("SignMe!!").buffer); + const computedMac = await cryptoFunctionService.hmacFast( + dataByteString, + keyByteString, + algorithm + ); + expect(Utils.fromBufferToHex(Utils.fromByteStringToArray(computedMac).buffer)).toBe(mac); + }); +} + +function testRsaGenerateKeyPair(length: 1024 | 2048 | 4096) { + it( + "should successfully generate a " + length + " bit key pair", + async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const keyPair = await cryptoFunctionService.rsaGenerateKeyPair(length); + expect(keyPair[0] == null || keyPair[1] == null).toBe(false); + const publicKey = await cryptoFunctionService.rsaExtractPublicKey(keyPair[1]); + expect(Utils.fromBufferToB64(keyPair[0])).toBe(Utils.fromBufferToB64(publicKey)); + }, + 30000 + ); +} + +function getWebCryptoFunctionService() { + const platformUtilsMock = Substitute.for(); + platformUtilsMock.isEdge().mimicks(() => navigator.userAgent.indexOf(" Edg/") !== -1); + + return new WebCryptoFunctionService(window); +} + +function makeStaticByteArray(length: number) { + const arr = new Uint8Array(length); + for (let i = 0; i < length; i++) { + arr[i] = i; + } + return arr; +} diff --git a/jslib/common/src/abstractions/api.service.ts b/jslib/common/src/abstractions/api.service.ts new file mode 100644 index 00000000..c9970ce8 --- /dev/null +++ b/jslib/common/src/abstractions/api.service.ts @@ -0,0 +1,679 @@ +import { PolicyType } from "../enums/policyType"; +import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest"; +import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest"; +import { AttachmentRequest } from "../models/request/attachmentRequest"; +import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest"; +import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest"; +import { CipherBulkMoveRequest } from "../models/request/cipherBulkMoveRequest"; +import { CipherBulkRestoreRequest } from "../models/request/cipherBulkRestoreRequest"; +import { CipherBulkShareRequest } from "../models/request/cipherBulkShareRequest"; +import { CipherCollectionsRequest } from "../models/request/cipherCollectionsRequest"; +import { CipherCreateRequest } from "../models/request/cipherCreateRequest"; +import { CipherRequest } from "../models/request/cipherRequest"; +import { CipherShareRequest } from "../models/request/cipherShareRequest"; +import { CollectionRequest } from "../models/request/collectionRequest"; +import { DeleteRecoverRequest } from "../models/request/deleteRecoverRequest"; +import { EmailRequest } from "../models/request/emailRequest"; +import { EmailTokenRequest } from "../models/request/emailTokenRequest"; +import { EmergencyAccessAcceptRequest } from "../models/request/emergencyAccessAcceptRequest"; +import { EmergencyAccessConfirmRequest } from "../models/request/emergencyAccessConfirmRequest"; +import { EmergencyAccessInviteRequest } from "../models/request/emergencyAccessInviteRequest"; +import { EmergencyAccessPasswordRequest } from "../models/request/emergencyAccessPasswordRequest"; +import { EmergencyAccessUpdateRequest } from "../models/request/emergencyAccessUpdateRequest"; +import { EventRequest } from "../models/request/eventRequest"; +import { FolderRequest } from "../models/request/folderRequest"; +import { GroupRequest } from "../models/request/groupRequest"; +import { IapCheckRequest } from "../models/request/iapCheckRequest"; +import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest"; +import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest"; +import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest"; +import { ImportCiphersRequest } from "../models/request/importCiphersRequest"; +import { ImportDirectoryRequest } from "../models/request/importDirectoryRequest"; +import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest"; +import { KdfRequest } from "../models/request/kdfRequest"; +import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest"; +import { KeysRequest } from "../models/request/keysRequest"; +import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organizationSponsorshipCreateRequest"; +import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organizationSponsorshipRedeemRequest"; +import { OrganizationSsoRequest } from "../models/request/organization/organizationSsoRequest"; +import { OrganizationCreateRequest } from "../models/request/organizationCreateRequest"; +import { OrganizationImportRequest } from "../models/request/organizationImportRequest"; +import { OrganizationKeysRequest } from "../models/request/organizationKeysRequest"; +import { OrganizationSubscriptionUpdateRequest } from "../models/request/organizationSubscriptionUpdateRequest"; +import { OrganizationTaxInfoUpdateRequest } from "../models/request/organizationTaxInfoUpdateRequest"; +import { OrganizationUpdateRequest } from "../models/request/organizationUpdateRequest"; +import { OrganizationUpgradeRequest } from "../models/request/organizationUpgradeRequest"; +import { OrganizationUserAcceptRequest } from "../models/request/organizationUserAcceptRequest"; +import { OrganizationUserBulkConfirmRequest } from "../models/request/organizationUserBulkConfirmRequest"; +import { OrganizationUserBulkRequest } from "../models/request/organizationUserBulkRequest"; +import { OrganizationUserConfirmRequest } from "../models/request/organizationUserConfirmRequest"; +import { OrganizationUserInviteRequest } from "../models/request/organizationUserInviteRequest"; +import { OrganizationUserResetPasswordEnrollmentRequest } from "../models/request/organizationUserResetPasswordEnrollmentRequest"; +import { OrganizationUserResetPasswordRequest } from "../models/request/organizationUserResetPasswordRequest"; +import { OrganizationUserUpdateGroupsRequest } from "../models/request/organizationUserUpdateGroupsRequest"; +import { OrganizationUserUpdateRequest } from "../models/request/organizationUserUpdateRequest"; +import { PasswordHintRequest } from "../models/request/passwordHintRequest"; +import { PasswordRequest } from "../models/request/passwordRequest"; +import { PaymentRequest } from "../models/request/paymentRequest"; +import { PolicyRequest } from "../models/request/policyRequest"; +import { PreloginRequest } from "../models/request/preloginRequest"; +import { ProviderAddOrganizationRequest } from "../models/request/provider/providerAddOrganizationRequest"; +import { ProviderOrganizationCreateRequest } from "../models/request/provider/providerOrganizationCreateRequest"; +import { ProviderSetupRequest } from "../models/request/provider/providerSetupRequest"; +import { ProviderUpdateRequest } from "../models/request/provider/providerUpdateRequest"; +import { ProviderUserAcceptRequest } from "../models/request/provider/providerUserAcceptRequest"; +import { ProviderUserBulkConfirmRequest } from "../models/request/provider/providerUserBulkConfirmRequest"; +import { ProviderUserBulkRequest } from "../models/request/provider/providerUserBulkRequest"; +import { ProviderUserConfirmRequest } from "../models/request/provider/providerUserConfirmRequest"; +import { ProviderUserInviteRequest } from "../models/request/provider/providerUserInviteRequest"; +import { ProviderUserUpdateRequest } from "../models/request/provider/providerUserUpdateRequest"; +import { RegisterRequest } from "../models/request/registerRequest"; +import { SeatRequest } from "../models/request/seatRequest"; +import { SecretVerificationRequest } from "../models/request/secretVerificationRequest"; +import { SelectionReadOnlyRequest } from "../models/request/selectionReadOnlyRequest"; +import { SendAccessRequest } from "../models/request/sendAccessRequest"; +import { SendRequest } from "../models/request/sendRequest"; +import { SetPasswordRequest } from "../models/request/setPasswordRequest"; +import { StorageRequest } from "../models/request/storageRequest"; +import { TaxInfoUpdateRequest } from "../models/request/taxInfoUpdateRequest"; +import { TwoFactorEmailRequest } from "../models/request/twoFactorEmailRequest"; +import { TwoFactorProviderRequest } from "../models/request/twoFactorProviderRequest"; +import { TwoFactorRecoveryRequest } from "../models/request/twoFactorRecoveryRequest"; +import { UpdateDomainsRequest } from "../models/request/updateDomainsRequest"; +import { UpdateKeyRequest } from "../models/request/updateKeyRequest"; +import { UpdateProfileRequest } from "../models/request/updateProfileRequest"; +import { UpdateTempPasswordRequest } from "../models/request/updateTempPasswordRequest"; +import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/updateTwoFactorAuthenticatorRequest"; +import { UpdateTwoFactorDuoRequest } from "../models/request/updateTwoFactorDuoRequest"; +import { UpdateTwoFactorEmailRequest } from "../models/request/updateTwoFactorEmailRequest"; +import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/updateTwoFactorWebAuthnDeleteRequest"; +import { UpdateTwoFactorWebAuthnRequest } from "../models/request/updateTwoFactorWebAuthnRequest"; +import { UpdateTwoFactorYubioOtpRequest } from "../models/request/updateTwoFactorYubioOtpRequest"; +import { VerifyBankRequest } from "../models/request/verifyBankRequest"; +import { VerifyDeleteRecoverRequest } from "../models/request/verifyDeleteRecoverRequest"; +import { VerifyEmailRequest } from "../models/request/verifyEmailRequest"; +import { ApiKeyResponse } from "../models/response/apiKeyResponse"; +import { AttachmentResponse } from "../models/response/attachmentResponse"; +import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { BillingResponse } from "../models/response/billingResponse"; +import { BreachAccountResponse } from "../models/response/breachAccountResponse"; +import { CipherResponse } from "../models/response/cipherResponse"; +import { + CollectionGroupDetailsResponse, + CollectionResponse, +} from "../models/response/collectionResponse"; +import { DomainsResponse } from "../models/response/domainsResponse"; +import { + EmergencyAccessGranteeDetailsResponse, + EmergencyAccessGrantorDetailsResponse, + EmergencyAccessTakeoverResponse, + EmergencyAccessViewResponse, +} from "../models/response/emergencyAccessResponse"; +import { EventResponse } from "../models/response/eventResponse"; +import { FolderResponse } from "../models/response/folderResponse"; +import { GroupDetailsResponse, GroupResponse } from "../models/response/groupResponse"; +import { IdentityCaptchaResponse } from "../models/response/identityCaptchaResponse"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; +import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; +import { KeyConnectorUserKeyResponse } from "../models/response/keyConnectorUserKeyResponse"; +import { ListResponse } from "../models/response/listResponse"; +import { OrganizationSsoResponse } from "../models/response/organization/organizationSsoResponse"; +import { OrganizationAutoEnrollStatusResponse } from "../models/response/organizationAutoEnrollStatusResponse"; +import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse"; +import { OrganizationResponse } from "../models/response/organizationResponse"; +import { OrganizationSubscriptionResponse } from "../models/response/organizationSubscriptionResponse"; +import { OrganizationUserBulkPublicKeyResponse } from "../models/response/organizationUserBulkPublicKeyResponse"; +import { OrganizationUserBulkResponse } from "../models/response/organizationUserBulkResponse"; +import { + OrganizationUserDetailsResponse, + OrganizationUserResetPasswordDetailsReponse, + OrganizationUserUserDetailsResponse, +} from "../models/response/organizationUserResponse"; +import { PaymentResponse } from "../models/response/paymentResponse"; +import { PlanResponse } from "../models/response/planResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; +import { PreloginResponse } from "../models/response/preloginResponse"; +import { ProfileResponse } from "../models/response/profileResponse"; +import { + ProviderOrganizationOrganizationDetailsResponse, + ProviderOrganizationResponse, +} from "../models/response/provider/providerOrganizationResponse"; +import { ProviderResponse } from "../models/response/provider/providerResponse"; +import { ProviderUserBulkPublicKeyResponse } from "../models/response/provider/providerUserBulkPublicKeyResponse"; +import { ProviderUserBulkResponse } from "../models/response/provider/providerUserBulkResponse"; +import { + ProviderUserResponse, + ProviderUserUserDetailsResponse, +} from "../models/response/provider/providerUserResponse"; +import { SelectionReadOnlyResponse } from "../models/response/selectionReadOnlyResponse"; +import { SendAccessResponse } from "../models/response/sendAccessResponse"; +import { SendFileDownloadDataResponse } from "../models/response/sendFileDownloadDataResponse"; +import { SendFileUploadDataResponse } from "../models/response/sendFileUploadDataResponse"; +import { SendResponse } from "../models/response/sendResponse"; +import { SubscriptionResponse } from "../models/response/subscriptionResponse"; +import { SyncResponse } from "../models/response/syncResponse"; +import { TaxInfoResponse } from "../models/response/taxInfoResponse"; +import { TaxRateResponse } from "../models/response/taxRateResponse"; +import { TwoFactorAuthenticatorResponse } from "../models/response/twoFactorAuthenticatorResponse"; +import { TwoFactorDuoResponse } from "../models/response/twoFactorDuoResponse"; +import { TwoFactorEmailResponse } from "../models/response/twoFactorEmailResponse"; +import { TwoFactorProviderResponse } from "../models/response/twoFactorProviderResponse"; +import { TwoFactorRecoverResponse } from "../models/response/twoFactorRescoverResponse"; +import { + ChallengeResponse, + TwoFactorWebAuthnResponse, +} from "../models/response/twoFactorWebAuthnResponse"; +import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyResponse"; +import { UserKeyResponse } from "../models/response/userKeyResponse"; +import { SendAccessView } from "../models/view/sendAccessView"; + +export abstract class ApiService { + postIdentityToken: ( + request: PasswordTokenRequest | SsoTokenRequest | ApiTokenRequest + ) => Promise; + refreshIdentityToken: () => Promise; + + getProfile: () => Promise; + getUserBilling: () => Promise; + getUserSubscription: () => Promise; + getTaxInfo: () => Promise; + putProfile: (request: UpdateProfileRequest) => Promise; + putTaxInfo: (request: TaxInfoUpdateRequest) => Promise; + postPrelogin: (request: PreloginRequest) => Promise; + postEmailToken: (request: EmailTokenRequest) => Promise; + postEmail: (request: EmailRequest) => Promise; + postPassword: (request: PasswordRequest) => Promise; + setPassword: (request: SetPasswordRequest) => Promise; + postSetKeyConnectorKey: (request: SetKeyConnectorKeyRequest) => Promise; + postSecurityStamp: (request: SecretVerificationRequest) => Promise; + deleteAccount: (request: SecretVerificationRequest) => Promise; + getAccountRevisionDate: () => Promise; + postPasswordHint: (request: PasswordHintRequest) => Promise; + postRegister: (request: RegisterRequest) => Promise; + postPremium: (data: FormData) => Promise; + postIapCheck: (request: IapCheckRequest) => Promise; + postReinstatePremium: () => Promise; + postCancelPremium: () => Promise; + postAccountStorage: (request: StorageRequest) => Promise; + postAccountPayment: (request: PaymentRequest) => Promise; + postAccountLicense: (data: FormData) => Promise; + postAccountKey: (request: UpdateKeyRequest) => Promise; + postAccountKeys: (request: KeysRequest) => Promise; + postAccountVerifyEmail: () => Promise; + postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise; + postAccountVerifyPassword: (request: SecretVerificationRequest) => Promise; + postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise; + postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise; + postAccountKdf: (request: KdfRequest) => Promise; + postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise; + postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise; + putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise; + postAccountRequestOTP: () => Promise; + postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise; + postConvertToKeyConnector: () => Promise; + + getFolder: (id: string) => Promise; + postFolder: (request: FolderRequest) => Promise; + putFolder: (id: string, request: FolderRequest) => Promise; + deleteFolder: (id: string) => Promise; + + getSend: (id: string) => Promise; + postSendAccess: ( + id: string, + request: SendAccessRequest, + apiUrl?: string + ) => Promise; + getSends: () => Promise>; + postSend: (request: SendRequest) => Promise; + postFileTypeSend: (request: SendRequest) => Promise; + postSendFile: (sendId: string, fileId: string, data: FormData) => Promise; + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + postSendFileLegacy: (data: FormData) => Promise; + putSend: (id: string, request: SendRequest) => Promise; + putSendRemovePassword: (id: string) => Promise; + deleteSend: (id: string) => Promise; + getSendFileDownloadData: ( + send: SendAccessView, + request: SendAccessRequest, + apiUrl?: string + ) => Promise; + renewSendFileUploadUrl: (sendId: string, fileId: string) => Promise; + + getCipher: (id: string) => Promise; + getCipherAdmin: (id: string) => Promise; + getAttachmentData: ( + cipherId: string, + attachmentId: string, + emergencyAccessId?: string + ) => Promise; + getCiphersOrganization: (organizationId: string) => Promise>; + postCipher: (request: CipherRequest) => Promise; + postCipherCreate: (request: CipherCreateRequest) => Promise; + postCipherAdmin: (request: CipherCreateRequest) => Promise; + putCipher: (id: string, request: CipherRequest) => Promise; + putCipherAdmin: (id: string, request: CipherRequest) => Promise; + deleteCipher: (id: string) => Promise; + deleteCipherAdmin: (id: string) => Promise; + deleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise; + deleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise; + putMoveCiphers: (request: CipherBulkMoveRequest) => Promise; + putShareCipher: (id: string, request: CipherShareRequest) => Promise; + putShareCiphers: (request: CipherBulkShareRequest) => Promise; + putCipherCollections: (id: string, request: CipherCollectionsRequest) => Promise; + putCipherCollectionsAdmin: (id: string, request: CipherCollectionsRequest) => Promise; + postPurgeCiphers: (request: SecretVerificationRequest, organizationId?: string) => Promise; + postImportCiphers: (request: ImportCiphersRequest) => Promise; + postImportOrganizationCiphers: ( + organizationId: string, + request: ImportOrganizationCiphersRequest + ) => Promise; + putDeleteCipher: (id: string) => Promise; + putDeleteCipherAdmin: (id: string) => Promise; + putDeleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise; + putDeleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise; + putRestoreCipher: (id: string) => Promise; + putRestoreCipherAdmin: (id: string) => Promise; + putRestoreManyCiphers: ( + request: CipherBulkRestoreRequest + ) => Promise>; + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + postCipherAttachmentLegacy: (id: string, data: FormData) => Promise; + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + postCipherAttachmentAdminLegacy: (id: string, data: FormData) => Promise; + postCipherAttachment: ( + id: string, + request: AttachmentRequest + ) => Promise; + deleteCipherAttachment: (id: string, attachmentId: string) => Promise; + deleteCipherAttachmentAdmin: (id: string, attachmentId: string) => Promise; + postShareCipherAttachment: ( + id: string, + attachmentId: string, + data: FormData, + organizationId: string + ) => Promise; + renewAttachmentUploadUrl: ( + id: string, + attachmentId: string + ) => Promise; + postAttachmentFile: (id: string, attachmentId: string, data: FormData) => Promise; + + getCollectionDetails: ( + organizationId: string, + id: string + ) => Promise; + getUserCollections: () => Promise>; + getCollections: (organizationId: string) => Promise>; + getCollectionUsers: (organizationId: string, id: string) => Promise; + postCollection: ( + organizationId: string, + request: CollectionRequest + ) => Promise; + putCollectionUsers: ( + organizationId: string, + id: string, + request: SelectionReadOnlyRequest[] + ) => Promise; + putCollection: ( + organizationId: string, + id: string, + request: CollectionRequest + ) => Promise; + deleteCollection: (organizationId: string, id: string) => Promise; + deleteCollectionUser: ( + organizationId: string, + id: string, + organizationUserId: string + ) => Promise; + + getGroupDetails: (organizationId: string, id: string) => Promise; + getGroups: (organizationId: string) => Promise>; + getGroupUsers: (organizationId: string, id: string) => Promise; + postGroup: (organizationId: string, request: GroupRequest) => Promise; + putGroup: (organizationId: string, id: string, request: GroupRequest) => Promise; + putGroupUsers: (organizationId: string, id: string, request: string[]) => Promise; + deleteGroup: (organizationId: string, id: string) => Promise; + deleteGroupUser: (organizationId: string, id: string, organizationUserId: string) => Promise; + + getPolicy: (organizationId: string, type: PolicyType) => Promise; + getPolicies: (organizationId: string) => Promise>; + getPoliciesByToken: ( + organizationId: string, + token: string, + email: string, + organizationUserId: string + ) => Promise>; + getPoliciesByInvitedUser: ( + organizationId: string, + userId: string + ) => Promise>; + putPolicy: ( + organizationId: string, + type: PolicyType, + request: PolicyRequest + ) => Promise; + + getOrganizationUser: ( + organizationId: string, + id: string + ) => Promise; + getOrganizationUserGroups: (organizationId: string, id: string) => Promise; + getOrganizationUsers: ( + organizationId: string + ) => Promise>; + getOrganizationUserResetPasswordDetails: ( + organizationId: string, + id: string + ) => Promise; + postOrganizationUserInvite: ( + organizationId: string, + request: OrganizationUserInviteRequest + ) => Promise; + postOrganizationUserReinvite: (organizationId: string, id: string) => Promise; + postManyOrganizationUserReinvite: ( + organizationId: string, + request: OrganizationUserBulkRequest + ) => Promise>; + postOrganizationUserAccept: ( + organizationId: string, + id: string, + request: OrganizationUserAcceptRequest + ) => Promise; + postOrganizationUserConfirm: ( + organizationId: string, + id: string, + request: OrganizationUserConfirmRequest + ) => Promise; + postOrganizationUsersPublicKey: ( + organizationId: string, + request: OrganizationUserBulkRequest + ) => Promise>; + postOrganizationUserBulkConfirm: ( + organizationId: string, + request: OrganizationUserBulkConfirmRequest + ) => Promise>; + + putOrganizationUser: ( + organizationId: string, + id: string, + request: OrganizationUserUpdateRequest + ) => Promise; + putOrganizationUserGroups: ( + organizationId: string, + id: string, + request: OrganizationUserUpdateGroupsRequest + ) => Promise; + putOrganizationUserResetPasswordEnrollment: ( + organizationId: string, + userId: string, + request: OrganizationUserResetPasswordEnrollmentRequest + ) => Promise; + putOrganizationUserResetPassword: ( + organizationId: string, + id: string, + request: OrganizationUserResetPasswordRequest + ) => Promise; + deleteOrganizationUser: (organizationId: string, id: string) => Promise; + deleteManyOrganizationUsers: ( + organizationId: string, + request: OrganizationUserBulkRequest + ) => Promise>; + + getSync: () => Promise; + postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise; + postPublicImportDirectory: (request: OrganizationImportRequest) => Promise; + + getSettingsDomains: () => Promise; + putSettingsDomains: (request: UpdateDomainsRequest) => Promise; + + getTwoFactorProviders: () => Promise>; + getTwoFactorOrganizationProviders: ( + organizationId: string + ) => Promise>; + getTwoFactorAuthenticator: ( + request: SecretVerificationRequest + ) => Promise; + getTwoFactorEmail: (request: SecretVerificationRequest) => Promise; + getTwoFactorDuo: (request: SecretVerificationRequest) => Promise; + getTwoFactorOrganizationDuo: ( + organizationId: string, + request: SecretVerificationRequest + ) => Promise; + getTwoFactorYubiKey: (request: SecretVerificationRequest) => Promise; + getTwoFactorWebAuthn: (request: SecretVerificationRequest) => Promise; + getTwoFactorWebAuthnChallenge: (request: SecretVerificationRequest) => Promise; + getTwoFactorRecover: (request: SecretVerificationRequest) => Promise; + putTwoFactorAuthenticator: ( + request: UpdateTwoFactorAuthenticatorRequest + ) => Promise; + putTwoFactorEmail: (request: UpdateTwoFactorEmailRequest) => Promise; + putTwoFactorDuo: (request: UpdateTwoFactorDuoRequest) => Promise; + putTwoFactorOrganizationDuo: ( + organizationId: string, + request: UpdateTwoFactorDuoRequest + ) => Promise; + putTwoFactorYubiKey: ( + request: UpdateTwoFactorYubioOtpRequest + ) => Promise; + putTwoFactorWebAuthn: ( + request: UpdateTwoFactorWebAuthnRequest + ) => Promise; + deleteTwoFactorWebAuthn: ( + request: UpdateTwoFactorWebAuthnDeleteRequest + ) => Promise; + putTwoFactorDisable: (request: TwoFactorProviderRequest) => Promise; + putTwoFactorOrganizationDisable: ( + organizationId: string, + request: TwoFactorProviderRequest + ) => Promise; + postTwoFactorRecover: (request: TwoFactorRecoveryRequest) => Promise; + postTwoFactorEmailSetup: (request: TwoFactorEmailRequest) => Promise; + postTwoFactorEmail: (request: TwoFactorEmailRequest) => Promise; + + getEmergencyAccessTrusted: () => Promise>; + getEmergencyAccessGranted: () => Promise>; + getEmergencyAccess: (id: string) => Promise; + getEmergencyGrantorPolicies: (id: string) => Promise>; + putEmergencyAccess: (id: string, request: EmergencyAccessUpdateRequest) => Promise; + deleteEmergencyAccess: (id: string) => Promise; + postEmergencyAccessInvite: (request: EmergencyAccessInviteRequest) => Promise; + postEmergencyAccessReinvite: (id: string) => Promise; + postEmergencyAccessAccept: (id: string, request: EmergencyAccessAcceptRequest) => Promise; + postEmergencyAccessConfirm: (id: string, request: EmergencyAccessConfirmRequest) => Promise; + postEmergencyAccessInitiate: (id: string) => Promise; + postEmergencyAccessApprove: (id: string) => Promise; + postEmergencyAccessReject: (id: string) => Promise; + postEmergencyAccessTakeover: (id: string) => Promise; + postEmergencyAccessPassword: ( + id: string, + request: EmergencyAccessPasswordRequest + ) => Promise; + postEmergencyAccessView: (id: string) => Promise; + + getOrganization: (id: string) => Promise; + getOrganizationBilling: (id: string) => Promise; + getOrganizationSubscription: (id: string) => Promise; + getOrganizationLicense: (id: string, installationId: string) => Promise; + getOrganizationTaxInfo: (id: string) => Promise; + getOrganizationAutoEnrollStatus: ( + identifier: string + ) => Promise; + getOrganizationSso: (id: string) => Promise; + postOrganization: (request: OrganizationCreateRequest) => Promise; + putOrganization: ( + id: string, + request: OrganizationUpdateRequest + ) => Promise; + putOrganizationTaxInfo: (id: string, request: OrganizationTaxInfoUpdateRequest) => Promise; + postLeaveOrganization: (id: string) => Promise; + postOrganizationLicense: (data: FormData) => Promise; + postOrganizationLicenseUpdate: (id: string, data: FormData) => Promise; + postOrganizationApiKey: ( + id: string, + request: SecretVerificationRequest + ) => Promise; + postOrganizationRotateApiKey: ( + id: string, + request: SecretVerificationRequest + ) => Promise; + postOrganizationSso: ( + id: string, + request: OrganizationSsoRequest + ) => Promise; + postOrganizationUpgrade: ( + id: string, + request: OrganizationUpgradeRequest + ) => Promise; + postOrganizationUpdateSubscription: ( + id: string, + request: OrganizationSubscriptionUpdateRequest + ) => Promise; + postOrganizationSeat: (id: string, request: SeatRequest) => Promise; + postOrganizationStorage: (id: string, request: StorageRequest) => Promise; + postOrganizationPayment: (id: string, request: PaymentRequest) => Promise; + postOrganizationVerifyBank: (id: string, request: VerifyBankRequest) => Promise; + postOrganizationCancel: (id: string) => Promise; + postOrganizationReinstate: (id: string) => Promise; + deleteOrganization: (id: string, request: SecretVerificationRequest) => Promise; + getPlans: () => Promise>; + getTaxRates: () => Promise>; + getOrganizationKeys: (id: string) => Promise; + postOrganizationKeys: ( + id: string, + request: OrganizationKeysRequest + ) => Promise; + + postProviderSetup: (id: string, request: ProviderSetupRequest) => Promise; + getProvider: (id: string) => Promise; + putProvider: (id: string, request: ProviderUpdateRequest) => Promise; + + getProviderUsers: (providerId: string) => Promise>; + getProviderUser: (providerId: string, id: string) => Promise; + postProviderUserInvite: (providerId: string, request: ProviderUserInviteRequest) => Promise; + postProviderUserReinvite: (providerId: string, id: string) => Promise; + postManyProviderUserReinvite: ( + providerId: string, + request: ProviderUserBulkRequest + ) => Promise>; + postProviderUserAccept: ( + providerId: string, + id: string, + request: ProviderUserAcceptRequest + ) => Promise; + postProviderUserConfirm: ( + providerId: string, + id: string, + request: ProviderUserConfirmRequest + ) => Promise; + postProviderUsersPublicKey: ( + providerId: string, + request: ProviderUserBulkRequest + ) => Promise>; + postProviderUserBulkConfirm: ( + providerId: string, + request: ProviderUserBulkConfirmRequest + ) => Promise>; + putProviderUser: ( + providerId: string, + id: string, + request: ProviderUserUpdateRequest + ) => Promise; + deleteProviderUser: (organizationId: string, id: string) => Promise; + deleteManyProviderUsers: ( + providerId: string, + request: ProviderUserBulkRequest + ) => Promise>; + getProviderClients: ( + providerId: string + ) => Promise>; + postProviderAddOrganization: ( + providerId: string, + request: ProviderAddOrganizationRequest + ) => Promise; + postProviderCreateOrganization: ( + providerId: string, + request: ProviderOrganizationCreateRequest + ) => Promise; + deleteProviderOrganization: (providerId: string, organizationId: string) => Promise; + + getEvents: (start: string, end: string, token: string) => Promise>; + getEventsCipher: ( + id: string, + start: string, + end: string, + token: string + ) => Promise>; + getEventsOrganization: ( + id: string, + start: string, + end: string, + token: string + ) => Promise>; + getEventsOrganizationUser: ( + organizationId: string, + id: string, + start: string, + end: string, + token: string + ) => Promise>; + getEventsProvider: ( + id: string, + start: string, + end: string, + token: string + ) => Promise>; + getEventsProviderUser: ( + providerId: string, + id: string, + start: string, + end: string, + token: string + ) => Promise>; + postEventsCollect: (request: EventRequest[]) => Promise; + + deleteSsoUser: (organizationId: string) => Promise; + getSsoUserIdentifier: () => Promise; + + getUserPublicKey: (id: string) => Promise; + + getHibpBreach: (username: string) => Promise; + + postBitPayInvoice: (request: BitPayInvoiceRequest) => Promise; + postSetupPayment: () => Promise; + + getActiveBearerToken: () => Promise; + fetch: (request: Request) => Promise; + nativeFetch: (request: Request) => Promise; + + preValidateSso: (identifier: string) => Promise; + + postCreateSponsorship: ( + sponsorshipOrgId: string, + request: OrganizationSponsorshipCreateRequest + ) => Promise; + deleteRevokeSponsorship: (sponsoringOrganizationId: string) => Promise; + deleteRemoveSponsorship: (sponsoringOrgId: string) => Promise; + postPreValidateSponsorshipToken: (sponsorshipToken: string) => Promise; + postRedeemSponsorship: ( + sponsorshipToken: string, + request: OrganizationSponsorshipRedeemRequest + ) => Promise; + postResendSponsorshipOffer: (sponsoringOrgId: string) => Promise; + + getUserKeyFromKeyConnector: (keyConnectorUrl: string) => Promise; + postUserKeyToKeyConnector: ( + keyConnectorUrl: string, + request: KeyConnectorUserKeyRequest + ) => Promise; + getKeyConnectorAlive: (keyConnectorUrl: string) => Promise; +} diff --git a/jslib/common/src/abstractions/appId.service.ts b/jslib/common/src/abstractions/appId.service.ts new file mode 100644 index 00000000..99bc6c9e --- /dev/null +++ b/jslib/common/src/abstractions/appId.service.ts @@ -0,0 +1,4 @@ +export abstract class AppIdService { + getAppId: () => Promise; + getAnonymousAppId: () => Promise; +} diff --git a/jslib/common/src/abstractions/audit.service.ts b/jslib/common/src/abstractions/audit.service.ts new file mode 100644 index 00000000..6b6b81f6 --- /dev/null +++ b/jslib/common/src/abstractions/audit.service.ts @@ -0,0 +1,6 @@ +import { BreachAccountResponse } from "../models/response/breachAccountResponse"; + +export abstract class AuditService { + passwordLeaked: (password: string) => Promise; + breachedAccounts: (username: string) => Promise; +} diff --git a/jslib/common/src/abstractions/auth.service.ts b/jslib/common/src/abstractions/auth.service.ts new file mode 100644 index 00000000..bebac730 --- /dev/null +++ b/jslib/common/src/abstractions/auth.service.ts @@ -0,0 +1,25 @@ +import { AuthResult } from "../models/domain/authResult"; +import { + ApiLogInCredentials, + PasswordLogInCredentials, + SsoLogInCredentials, +} from "../models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor"; + +export abstract class AuthService { + masterPasswordHash: string; + email: string; + logIn: ( + credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + ) => Promise; + logInTwoFactor: ( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string + ) => Promise; + logOut: (callback: () => void) => void; + makePreloginKey: (masterPassword: string, email: string) => Promise; + authingWithApiKey: () => boolean; + authingWithSso: () => boolean; + authingWithPassword: () => boolean; +} diff --git a/jslib/common/src/abstractions/broadcaster.service.ts b/jslib/common/src/abstractions/broadcaster.service.ts new file mode 100644 index 00000000..1b9d0899 --- /dev/null +++ b/jslib/common/src/abstractions/broadcaster.service.ts @@ -0,0 +1,5 @@ +export abstract class BroadcasterService { + send: (message: any, id?: string) => void; + subscribe: (id: string, messageCallback: (message: any) => any) => void; + unsubscribe: (id: string) => void; +} diff --git a/jslib/common/src/abstractions/cipher.service.ts b/jslib/common/src/abstractions/cipher.service.ts new file mode 100644 index 00000000..a87a6f0a --- /dev/null +++ b/jslib/common/src/abstractions/cipher.service.ts @@ -0,0 +1,79 @@ +import { CipherType } from "../enums/cipherType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { CipherData } from "../models/data/cipherData"; +import { Cipher } from "../models/domain/cipher"; +import { Field } from "../models/domain/field"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { CipherView } from "../models/view/cipherView"; +import { FieldView } from "../models/view/fieldView"; + +export abstract class CipherService { + clearCache: (userId?: string) => Promise; + encrypt: ( + model: CipherView, + key?: SymmetricCryptoKey, + originalCipher?: Cipher + ) => Promise; + encryptFields: (fieldsModel: FieldView[], key: SymmetricCryptoKey) => Promise; + encryptField: (fieldModel: FieldView, key: SymmetricCryptoKey) => Promise; + get: (id: string) => Promise; + getAll: () => Promise; + getAllDecrypted: () => Promise; + getAllDecryptedForGrouping: (groupingId: string, folder?: boolean) => Promise; + getAllDecryptedForUrl: ( + url: string, + includeOtherTypes?: CipherType[], + defaultMatch?: UriMatchType + ) => Promise; + getAllFromApiForOrganization: (organizationId: string) => Promise; + getLastUsedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise; + getLastLaunchedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise; + getNextCipherForUrl: (url: string) => Promise; + updateLastUsedIndexForUrl: (url: string) => void; + updateLastUsedDate: (id: string) => Promise; + updateLastLaunchedDate: (id: string) => Promise; + saveNeverDomain: (domain: string) => Promise; + saveWithServer: (cipher: Cipher) => Promise; + shareWithServer: ( + cipher: CipherView, + organizationId: string, + collectionIds: string[] + ) => Promise; + shareManyWithServer: ( + ciphers: CipherView[], + organizationId: string, + collectionIds: string[] + ) => Promise; + saveAttachmentWithServer: ( + cipher: Cipher, + unencryptedFile: any, + admin?: boolean + ) => Promise; + saveAttachmentRawWithServer: ( + cipher: Cipher, + filename: string, + data: ArrayBuffer, + admin?: boolean + ) => Promise; + saveCollectionsWithServer: (cipher: Cipher) => Promise; + upsert: (cipher: CipherData | CipherData[]) => Promise; + replace: (ciphers: { [id: string]: CipherData }) => Promise; + clear: (userId: string) => Promise; + moveManyWithServer: (ids: string[], folderId: string) => Promise; + delete: (id: string | string[]) => Promise; + deleteWithServer: (id: string) => Promise; + deleteManyWithServer: (ids: string[]) => Promise; + deleteAttachment: (id: string, attachmentId: string) => Promise; + deleteAttachmentWithServer: (id: string, attachmentId: string) => Promise; + sortCiphersByLastUsed: (a: any, b: any) => number; + sortCiphersByLastUsedThenName: (a: any, b: any) => number; + getLocaleSortingFunction: () => (a: CipherView, b: CipherView) => number; + softDelete: (id: string | string[]) => Promise; + softDeleteWithServer: (id: string) => Promise; + softDeleteManyWithServer: (ids: string[]) => Promise; + restore: ( + cipher: { id: string; revisionDate: string } | { id: string; revisionDate: string }[] + ) => Promise; + restoreWithServer: (id: string) => Promise; + restoreManyWithServer: (ids: string[]) => Promise; +} diff --git a/jslib/common/src/abstractions/collection.service.ts b/jslib/common/src/abstractions/collection.service.ts new file mode 100644 index 00000000..0673e906 --- /dev/null +++ b/jslib/common/src/abstractions/collection.service.ts @@ -0,0 +1,19 @@ +import { CollectionData } from "../models/data/collectionData"; +import { Collection } from "../models/domain/collection"; +import { TreeNode } from "../models/domain/treeNode"; +import { CollectionView } from "../models/view/collectionView"; + +export abstract class CollectionService { + clearCache: (userId?: string) => Promise; + encrypt: (model: CollectionView) => Promise; + decryptMany: (collections: Collection[]) => Promise; + get: (id: string) => Promise; + getAll: () => Promise; + getAllDecrypted: () => Promise; + getAllNested: (collections?: CollectionView[]) => Promise[]>; + getNested: (id: string) => Promise>; + upsert: (collection: CollectionData | CollectionData[]) => Promise; + replace: (collections: { [id: string]: CollectionData }) => Promise; + clear: (userId: string) => Promise; + delete: (id: string | string[]) => Promise; +} diff --git a/jslib/common/src/abstractions/crypto.service.ts b/jslib/common/src/abstractions/crypto.service.ts new file mode 100644 index 00000000..bc61ba7e --- /dev/null +++ b/jslib/common/src/abstractions/crypto.service.ts @@ -0,0 +1,86 @@ +import { HashPurpose } from "../enums/hashPurpose"; +import { KdfType } from "../enums/kdfType"; +import { KeySuffixOptions } from "../enums/keySuffixOptions"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { ProfileOrganizationResponse } from "../models/response/profileOrganizationResponse"; +import { ProfileProviderOrganizationResponse } from "../models/response/profileProviderOrganizationResponse"; +import { ProfileProviderResponse } from "../models/response/profileProviderResponse"; + +export abstract class CryptoService { + setKey: (key: SymmetricCryptoKey) => Promise; + setKeyHash: (keyHash: string) => Promise; + setEncKey: (encKey: string) => Promise; + setEncPrivateKey: (encPrivateKey: string) => Promise; + setOrgKeys: ( + orgs: ProfileOrganizationResponse[], + providerOrgs: ProfileProviderOrganizationResponse[] + ) => Promise; + setProviderKeys: (orgs: ProfileProviderResponse[]) => Promise; + getKey: (keySuffix?: KeySuffixOptions, userId?: string) => Promise; + getKeyFromStorage: (keySuffix: KeySuffixOptions, userId?: string) => Promise; + getKeyHash: () => Promise; + compareAndUpdateKeyHash: (masterPassword: string, key: SymmetricCryptoKey) => Promise; + getEncKey: (key?: SymmetricCryptoKey) => Promise; + getPublicKey: () => Promise; + getPrivateKey: () => Promise; + getFingerprint: (userId: string, publicKey?: ArrayBuffer) => Promise; + getOrgKeys: () => Promise>; + getOrgKey: (orgId: string) => Promise; + getProviderKey: (providerId: string) => Promise; + hasKey: () => Promise; + hasKeyInMemory: (userId?: string) => Promise; + hasKeyStored: (keySuffix?: KeySuffixOptions, userId?: string) => Promise; + hasEncKey: () => Promise; + clearKey: (clearSecretStorage?: boolean, userId?: string) => Promise; + clearKeyHash: () => Promise; + clearEncKey: (memoryOnly?: boolean, userId?: string) => Promise; + clearKeyPair: (memoryOnly?: boolean, userId?: string) => Promise; + clearOrgKeys: (memoryOnly?: boolean, userId?: string) => Promise; + clearProviderKeys: (memoryOnly?: boolean) => Promise; + clearPinProtectedKey: () => Promise; + clearKeys: (userId?: string) => Promise; + toggleKey: () => Promise; + makeKey: ( + password: string, + salt: string, + kdf: KdfType, + kdfIterations: number + ) => Promise; + makeKeyFromPin: ( + pin: string, + salt: string, + kdf: KdfType, + kdfIterations: number, + protectedKeyCs?: EncString + ) => Promise; + makeShareKey: () => Promise<[EncString, SymmetricCryptoKey]>; + makeKeyPair: (key?: SymmetricCryptoKey) => Promise<[string, EncString]>; + makePinKey: ( + pin: string, + salt: string, + kdf: KdfType, + kdfIterations: number + ) => Promise; + makeSendKey: (keyMaterial: ArrayBuffer) => Promise; + hashPassword: ( + password: string, + key: SymmetricCryptoKey, + hashPurpose?: HashPurpose + ) => Promise; + makeEncKey: (key: SymmetricCryptoKey) => Promise<[SymmetricCryptoKey, EncString]>; + remakeEncKey: ( + key: SymmetricCryptoKey, + encKey?: SymmetricCryptoKey + ) => Promise<[SymmetricCryptoKey, EncString]>; + encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise; + encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise; + rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise; + rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise; + decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise; + decryptToUtf8: (encString: EncString, key?: SymmetricCryptoKey) => Promise; + decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise; + randomNumber: (min: number, max: number) => Promise; + validateKey: (key: SymmetricCryptoKey) => Promise; +} diff --git a/jslib/common/src/abstractions/cryptoFunction.service.ts b/jslib/common/src/abstractions/cryptoFunction.service.ts new file mode 100644 index 00000000..21ed33cb --- /dev/null +++ b/jslib/common/src/abstractions/cryptoFunction.service.ts @@ -0,0 +1,62 @@ +import { DecryptParameters } from "../models/domain/decryptParameters"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; + +export abstract class CryptoFunctionService { + pbkdf2: ( + password: string | ArrayBuffer, + salt: string | ArrayBuffer, + algorithm: "sha256" | "sha512", + iterations: number + ) => Promise; + hkdf: ( + ikm: ArrayBuffer, + salt: string | ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ) => Promise; + hkdfExpand: ( + prk: ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ) => Promise; + hash: ( + value: string | ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" | "md5" + ) => Promise; + hmac: ( + value: ArrayBuffer, + key: ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" + ) => Promise; + compare: (a: ArrayBuffer, b: ArrayBuffer) => Promise; + hmacFast: ( + value: ArrayBuffer | string, + key: ArrayBuffer | string, + algorithm: "sha1" | "sha256" | "sha512" + ) => Promise; + compareFast: (a: ArrayBuffer | string, b: ArrayBuffer | string) => Promise; + aesEncrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; + aesDecryptFastParameters: ( + data: string, + iv: string, + mac: string, + key: SymmetricCryptoKey + ) => DecryptParameters; + aesDecryptFast: (parameters: DecryptParameters) => Promise; + aesDecrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; + rsaEncrypt: ( + data: ArrayBuffer, + publicKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ) => Promise; + rsaDecrypt: ( + data: ArrayBuffer, + privateKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ) => Promise; + rsaExtractPublicKey: (privateKey: ArrayBuffer) => Promise; + rsaGenerateKeyPair: (length: 1024 | 2048 | 4096) => Promise<[ArrayBuffer, ArrayBuffer]>; + randomBytes: (length: number) => Promise; +} diff --git a/jslib/common/src/abstractions/environment.service.ts b/jslib/common/src/abstractions/environment.service.ts new file mode 100644 index 00000000..8398b4c6 --- /dev/null +++ b/jslib/common/src/abstractions/environment.service.ts @@ -0,0 +1,34 @@ +import { Observable } from "rxjs"; + +export type Urls = { + base?: string; + webVault?: string; + api?: string; + identity?: string; + icons?: string; + notifications?: string; + events?: string; + keyConnector?: string; +}; + +export type PayPalConfig = { + businessId?: string; + buttonAction?: string; +}; + +export abstract class EnvironmentService { + urls: Observable; + + hasBaseUrl: () => boolean; + getNotificationsUrl: () => string; + getWebVaultUrl: () => string; + getSendUrl: () => string; + getIconsUrl: () => string; + getApiUrl: () => string; + getIdentityUrl: () => string; + getEventsUrl: () => string; + getKeyConnectorUrl: () => string; + setUrlsFromStorage: () => Promise; + setUrls: (urls: Urls) => Promise; + getUrls: () => Urls; +} diff --git a/jslib/common/src/abstractions/event.service.ts b/jslib/common/src/abstractions/event.service.ts new file mode 100644 index 00000000..2f7660fb --- /dev/null +++ b/jslib/common/src/abstractions/event.service.ts @@ -0,0 +1,7 @@ +import { EventType } from "../enums/eventType"; + +export abstract class EventService { + collect: (eventType: EventType, cipherId?: string, uploadImmediately?: boolean) => Promise; + uploadEvents: (userId?: string) => Promise; + clearEvents: (userId?: string) => Promise; +} diff --git a/jslib/common/src/abstractions/export.service.ts b/jslib/common/src/abstractions/export.service.ts new file mode 100644 index 00000000..b0266530 --- /dev/null +++ b/jslib/common/src/abstractions/export.service.ts @@ -0,0 +1,11 @@ +import { EventView } from "../models/view/eventView"; + +export type ExportFormat = "csv" | "json" | "encrypted_json"; + +export abstract class ExportService { + getExport: (format?: ExportFormat, organizationId?: string) => Promise; + getPasswordProtectedExport: (password: string, organizationId?: string) => Promise; + getOrganizationExport: (organizationId: string, format?: ExportFormat) => Promise; + getEventExport: (events: EventView[]) => Promise; + getFileName: (prefix?: string, extension?: string) => string; +} diff --git a/jslib/common/src/abstractions/fileUpload.service.ts b/jslib/common/src/abstractions/fileUpload.service.ts new file mode 100644 index 00000000..fcd3c22c --- /dev/null +++ b/jslib/common/src/abstractions/fileUpload.service.ts @@ -0,0 +1,18 @@ +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { SendFileUploadDataResponse } from "../models/response/sendFileUploadDataResponse"; + +export abstract class FileUploadService { + uploadSendFile: ( + uploadData: SendFileUploadDataResponse, + fileName: EncString, + encryptedFileData: EncArrayBuffer + ) => Promise; + uploadCipherAttachment: ( + admin: boolean, + uploadData: AttachmentUploadDataResponse, + fileName: EncString, + encryptedFileData: EncArrayBuffer + ) => Promise; +} diff --git a/jslib/common/src/abstractions/folder.service.ts b/jslib/common/src/abstractions/folder.service.ts new file mode 100644 index 00000000..574cebfc --- /dev/null +++ b/jslib/common/src/abstractions/folder.service.ts @@ -0,0 +1,21 @@ +import { FolderData } from "../models/data/folderData"; +import { Folder } from "../models/domain/folder"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { TreeNode } from "../models/domain/treeNode"; +import { FolderView } from "../models/view/folderView"; + +export abstract class FolderService { + clearCache: (userId?: string) => Promise; + encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise; + get: (id: string) => Promise; + getAll: () => Promise; + getAllDecrypted: () => Promise; + getAllNested: () => Promise[]>; + getNested: (id: string) => Promise>; + saveWithServer: (folder: Folder) => Promise; + upsert: (folder: FolderData | FolderData[]) => Promise; + replace: (folders: { [id: string]: FolderData }) => Promise; + clear: (userId: string) => Promise; + delete: (id: string | string[]) => Promise; + deleteWithServer: (id: string) => Promise; +} diff --git a/jslib/common/src/abstractions/i18n.service.ts b/jslib/common/src/abstractions/i18n.service.ts new file mode 100644 index 00000000..4706e0d1 --- /dev/null +++ b/jslib/common/src/abstractions/i18n.service.ts @@ -0,0 +1,9 @@ +export abstract class I18nService { + locale: string; + supportedTranslationLocales: string[]; + translationLocale: string; + collator: Intl.Collator; + localeNames: Map; + t: (id: string, p1?: string, p2?: string, p3?: string) => string; + translate: (id: string, p1?: string, p2?: string, p3?: string) => string; +} diff --git a/jslib/common/src/abstractions/import.service.ts b/jslib/common/src/abstractions/import.service.ts new file mode 100644 index 00000000..01f099b3 --- /dev/null +++ b/jslib/common/src/abstractions/import.service.ts @@ -0,0 +1,19 @@ +import { ImportOption, ImportType } from "../enums/importOptions"; +import { ImportError } from "../importers/importError"; +import { Importer } from "../importers/importer"; + +export abstract class ImportService { + featuredImportOptions: readonly ImportOption[]; + regularImportOptions: readonly ImportOption[]; + getImportOptions: () => ImportOption[]; + import: ( + importer: Importer, + fileContents: string, + organizationId?: string + ) => Promise; + getImporter: ( + format: ImportType | "bitwardenpasswordprotected", + organizationId: string, + password?: string + ) => Importer; +} diff --git a/jslib/common/src/abstractions/keyConnector.service.ts b/jslib/common/src/abstractions/keyConnector.service.ts new file mode 100644 index 00000000..b8f3bf7c --- /dev/null +++ b/jslib/common/src/abstractions/keyConnector.service.ts @@ -0,0 +1,19 @@ +import { Organization } from "../models/domain/organization"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; + +export abstract class KeyConnectorService { + getAndSetKey: (url?: string) => Promise; + getManagingOrganization: () => Promise; + getUsesKeyConnector: () => Promise; + migrateUser: () => Promise; + userNeedsMigration: () => Promise; + convertNewSsoUserToKeyConnector: ( + tokenResponse: IdentityTokenResponse, + orgId: string + ) => Promise; + setUsesKeyConnector: (enabled: boolean) => Promise; + setConvertAccountRequired: (status: boolean) => Promise; + getConvertAccountRequired: () => Promise; + removeConvertAccountRequired: () => Promise; + clear: () => Promise; +} diff --git a/jslib/common/src/abstractions/log.service.ts b/jslib/common/src/abstractions/log.service.ts new file mode 100644 index 00000000..9997e480 --- /dev/null +++ b/jslib/common/src/abstractions/log.service.ts @@ -0,0 +1,11 @@ +import { LogLevelType } from "../enums/logLevelType"; + +export abstract class LogService { + debug: (message: string) => void; + info: (message: string) => void; + warning: (message: string) => void; + error: (message: string) => void; + write: (level: LogLevelType, message: string) => void; + time: (label: string) => void; + timeEnd: (label: string) => [number, number]; +} diff --git a/jslib/common/src/abstractions/messaging.service.ts b/jslib/common/src/abstractions/messaging.service.ts new file mode 100644 index 00000000..7c5f05f9 --- /dev/null +++ b/jslib/common/src/abstractions/messaging.service.ts @@ -0,0 +1,3 @@ +export abstract class MessagingService { + send: (subscriber: string, arg?: any) => void; +} diff --git a/jslib/common/src/abstractions/notifications.service.ts b/jslib/common/src/abstractions/notifications.service.ts new file mode 100644 index 00000000..921e8e62 --- /dev/null +++ b/jslib/common/src/abstractions/notifications.service.ts @@ -0,0 +1,6 @@ +export abstract class NotificationsService { + init: () => Promise; + updateConnection: (sync?: boolean) => Promise; + reconnectFromActivity: () => Promise; + disconnectFromInactivity: () => Promise; +} diff --git a/jslib/common/src/abstractions/organization.service.ts b/jslib/common/src/abstractions/organization.service.ts new file mode 100644 index 00000000..e0b5fde4 --- /dev/null +++ b/jslib/common/src/abstractions/organization.service.ts @@ -0,0 +1,11 @@ +import { OrganizationData } from "../models/data/organizationData"; +import { Organization } from "../models/domain/organization"; + +export abstract class OrganizationService { + get: (id: string) => Promise; + getByIdentifier: (identifier: string) => Promise; + getAll: (userId?: string) => Promise; + save: (orgs: { [id: string]: OrganizationData }) => Promise; + canManageSponsorships: () => Promise; + hasOrganizations: (userId?: string) => Promise; +} diff --git a/jslib/common/src/abstractions/passwordGeneration.service.ts b/jslib/common/src/abstractions/passwordGeneration.service.ts new file mode 100644 index 00000000..82bc021f --- /dev/null +++ b/jslib/common/src/abstractions/passwordGeneration.service.ts @@ -0,0 +1,20 @@ +import * as zxcvbn from "zxcvbn"; + +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { PasswordGeneratorPolicyOptions } from "../models/domain/passwordGeneratorPolicyOptions"; + +export abstract class PasswordGenerationService { + generatePassword: (options: any) => Promise; + generatePassphrase: (options: any) => Promise; + getOptions: () => Promise<[any, PasswordGeneratorPolicyOptions]>; + enforcePasswordGeneratorPoliciesOnOptions: ( + options: any + ) => Promise<[any, PasswordGeneratorPolicyOptions]>; + getPasswordGeneratorPolicyOptions: () => Promise; + saveOptions: (options: any) => Promise; + getHistory: () => Promise; + addHistory: (password: string) => Promise; + clear: (userId?: string) => Promise; + passwordStrength: (password: string, userInputs?: string[]) => zxcvbn.ZXCVBNResult; + normalizeOptions: (options: any, enforcedPolicyOptions: PasswordGeneratorPolicyOptions) => void; +} diff --git a/jslib/common/src/abstractions/passwordReprompt.service.ts b/jslib/common/src/abstractions/passwordReprompt.service.ts new file mode 100644 index 00000000..6253425b --- /dev/null +++ b/jslib/common/src/abstractions/passwordReprompt.service.ts @@ -0,0 +1,5 @@ +export abstract class PasswordRepromptService { + protectedFields: () => string[]; + showPasswordPrompt: () => Promise; + enabled: () => Promise; +} diff --git a/jslib/common/src/abstractions/platformUtils.service.ts b/jslib/common/src/abstractions/platformUtils.service.ts new file mode 100644 index 00000000..4a014868 --- /dev/null +++ b/jslib/common/src/abstractions/platformUtils.service.ts @@ -0,0 +1,52 @@ +import { ClientType } from "../enums/clientType"; +import { DeviceType } from "../enums/deviceType"; +import { ThemeType } from "../enums/themeType"; + +interface ToastOptions { + timeout?: number; +} + +export abstract class PlatformUtilsService { + getDevice: () => DeviceType; + getDeviceString: () => string; + getClientType: () => ClientType; + isFirefox: () => boolean; + isChrome: () => boolean; + isEdge: () => boolean; + isOpera: () => boolean; + isVivaldi: () => boolean; + isSafari: () => boolean; + isMacAppStore: () => boolean; + isViewOpen: () => Promise; + launchUri: (uri: string, options?: any) => void; + saveFile: (win: Window, blobData: any, blobOptions: any, fileName: string) => void; + getApplicationVersion: () => Promise; + supportsWebAuthn: (win: Window) => boolean; + supportsDuo: () => boolean; + showToast: ( + type: "error" | "success" | "warning" | "info", + title: string, + text: string | string[], + options?: ToastOptions + ) => void; + showDialog: ( + body: string, + title?: string, + confirmText?: string, + cancelText?: string, + type?: string, + bodyIsHtml?: boolean + ) => Promise; + isDev: () => boolean; + isSelfHost: () => boolean; + copyToClipboard: (text: string, options?: any) => void | boolean; + readFromClipboard: (options?: any) => Promise; + supportsBiometric: () => Promise; + authenticateBiometric: () => Promise; + getDefaultSystemTheme: () => Promise; + onDefaultSystemThemeChange: ( + callback: (theme: ThemeType.Light | ThemeType.Dark) => unknown + ) => unknown; + getEffectiveTheme: () => Promise; + supportsSecureStorage: () => boolean; +} diff --git a/jslib/common/src/abstractions/policy.service.ts b/jslib/common/src/abstractions/policy.service.ts new file mode 100644 index 00000000..24dcc996 --- /dev/null +++ b/jslib/common/src/abstractions/policy.service.ts @@ -0,0 +1,32 @@ +import { PolicyType } from "../enums/policyType"; +import { PolicyData } from "../models/data/policyData"; +import { MasterPasswordPolicyOptions } from "../models/domain/masterPasswordPolicyOptions"; +import { Policy } from "../models/domain/policy"; +import { ResetPasswordPolicyOptions } from "../models/domain/resetPasswordPolicyOptions"; +import { ListResponse } from "../models/response/listResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; + +export abstract class PolicyService { + clearCache: () => void; + getAll: (type?: PolicyType, userId?: string) => Promise; + getPolicyForOrganization: (policyType: PolicyType, organizationId: string) => Promise; + replace: (policies: { [id: string]: PolicyData }) => Promise; + clear: (userId?: string) => Promise; + getMasterPasswordPoliciesForInvitedUsers: (orgId: string) => Promise; + getMasterPasswordPolicyOptions: (policies?: Policy[]) => Promise; + evaluateMasterPassword: ( + passwordStrength: number, + newPassword: string, + enforcedPolicyOptions?: MasterPasswordPolicyOptions + ) => boolean; + getResetPasswordPolicyOptions: ( + policies: Policy[], + orgId: string + ) => [ResetPasswordPolicyOptions, boolean]; + mapPoliciesFromToken: (policiesResponse: ListResponse) => Policy[]; + policyAppliesToUser: ( + policyType: PolicyType, + policyFilter?: (policy: Policy) => boolean, + userId?: string + ) => Promise; +} diff --git a/jslib/common/src/abstractions/provider.service.ts b/jslib/common/src/abstractions/provider.service.ts new file mode 100644 index 00000000..e4746369 --- /dev/null +++ b/jslib/common/src/abstractions/provider.service.ts @@ -0,0 +1,8 @@ +import { ProviderData } from "../models/data/providerData"; +import { Provider } from "../models/domain/provider"; + +export abstract class ProviderService { + get: (id: string) => Promise; + getAll: () => Promise; + save: (providers: { [id: string]: ProviderData }) => Promise; +} diff --git a/jslib/common/src/abstractions/search.service.ts b/jslib/common/src/abstractions/search.service.ts new file mode 100644 index 00000000..9c2b0eeb --- /dev/null +++ b/jslib/common/src/abstractions/search.service.ts @@ -0,0 +1,16 @@ +import { CipherView } from "../models/view/cipherView"; +import { SendView } from "../models/view/sendView"; + +export abstract class SearchService { + indexedEntityId?: string = null; + clearIndex: () => void; + isSearchable: (query: string) => boolean; + indexCiphers: (indexedEntityGuid?: string, ciphersToIndex?: CipherView[]) => Promise; + searchCiphers: ( + query: string, + filter?: ((cipher: CipherView) => boolean) | ((cipher: CipherView) => boolean)[], + ciphers?: CipherView[] + ) => Promise; + searchCiphersBasic: (ciphers: CipherView[], query: string, deleted?: boolean) => CipherView[]; + searchSends: (sends: SendView[], query: string) => SendView[]; +} diff --git a/jslib/common/src/abstractions/send.service.ts b/jslib/common/src/abstractions/send.service.ts new file mode 100644 index 00000000..3331e41e --- /dev/null +++ b/jslib/common/src/abstractions/send.service.ts @@ -0,0 +1,25 @@ +import { SendData } from "../models/data/sendData"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { Send } from "../models/domain/send"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { SendView } from "../models/view/sendView"; + +export abstract class SendService { + clearCache: () => Promise; + encrypt: ( + model: SendView, + file: File | ArrayBuffer, + password: string, + key?: SymmetricCryptoKey + ) => Promise<[Send, EncArrayBuffer]>; + get: (id: string) => Promise; + getAll: () => Promise; + getAllDecrypted: () => Promise; + saveWithServer: (sendData: [Send, EncArrayBuffer]) => Promise; + upsert: (send: SendData | SendData[]) => Promise; + replace: (sends: { [id: string]: SendData }) => Promise; + clear: (userId: string) => Promise; + delete: (id: string | string[]) => Promise; + deleteWithServer: (id: string) => Promise; + removePasswordWithServer: (id: string) => Promise; +} diff --git a/jslib/common/src/abstractions/settings.service.ts b/jslib/common/src/abstractions/settings.service.ts new file mode 100644 index 00000000..e7886585 --- /dev/null +++ b/jslib/common/src/abstractions/settings.service.ts @@ -0,0 +1,6 @@ +export abstract class SettingsService { + clearCache: () => Promise; + getEquivalentDomains: () => Promise; + setEquivalentDomains: (equivalentDomains: string[][]) => Promise; + clear: (userId?: string) => Promise; +} diff --git a/jslib/common/src/abstractions/state.service.ts b/jslib/common/src/abstractions/state.service.ts new file mode 100644 index 00000000..ebf94df3 --- /dev/null +++ b/jslib/common/src/abstractions/state.service.ts @@ -0,0 +1,307 @@ +import { BehaviorSubject } from "rxjs"; + +import { KdfType } from "../enums/kdfType"; +import { ThemeType } from "../enums/themeType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { EventData } from "../models/data/eventData"; +import { FolderData } from "../models/data/folderData"; +import { OrganizationData } from "../models/data/organizationData"; +import { PolicyData } from "../models/data/policyData"; +import { ProviderData } from "../models/data/providerData"; +import { SendData } from "../models/data/sendData"; +import { Account } from "../models/domain/account"; +import { EncString } from "../models/domain/encString"; +import { EnvironmentUrls } from "../models/domain/environmentUrls"; +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { Policy } from "../models/domain/policy"; +import { StorageOptions } from "../models/domain/storageOptions"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { WindowState } from "../models/domain/windowState"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { FolderView } from "../models/view/folderView"; +import { SendView } from "../models/view/sendView"; + +export abstract class StateService { + accounts: BehaviorSubject<{ [userId: string]: T }>; + activeAccount: BehaviorSubject; + + addAccount: (account: T) => Promise; + setActiveUser: (userId: string) => Promise; + clean: (options?: StorageOptions) => Promise; + init: () => Promise; + + getAccessToken: (options?: StorageOptions) => Promise; + setAccessToken: (value: string, options?: StorageOptions) => Promise; + getAddEditCipherInfo: (options?: StorageOptions) => Promise; + setAddEditCipherInfo: (value: any, options?: StorageOptions) => Promise; + getAlwaysShowDock: (options?: StorageOptions) => Promise; + setAlwaysShowDock: (value: boolean, options?: StorageOptions) => Promise; + getApiKeyClientId: (options?: StorageOptions) => Promise; + setApiKeyClientId: (value: string, options?: StorageOptions) => Promise; + getApiKeyClientSecret: (options?: StorageOptions) => Promise; + setApiKeyClientSecret: (value: string, options?: StorageOptions) => Promise; + getAutoConfirmFingerPrints: (options?: StorageOptions) => Promise; + setAutoConfirmFingerprints: (value: boolean, options?: StorageOptions) => Promise; + getAutoFillOnPageLoadDefault: (options?: StorageOptions) => Promise; + setAutoFillOnPageLoadDefault: (value: boolean, options?: StorageOptions) => Promise; + getBiometricAwaitingAcceptance: (options?: StorageOptions) => Promise; + setBiometricAwaitingAcceptance: (value: boolean, options?: StorageOptions) => Promise; + getBiometricFingerprintValidated: (options?: StorageOptions) => Promise; + setBiometricFingerprintValidated: (value: boolean, options?: StorageOptions) => Promise; + getBiometricLocked: (options?: StorageOptions) => Promise; + setBiometricLocked: (value: boolean, options?: StorageOptions) => Promise; + getBiometricText: (options?: StorageOptions) => Promise; + setBiometricText: (value: string, options?: StorageOptions) => Promise; + getBiometricUnlock: (options?: StorageOptions) => Promise; + setBiometricUnlock: (value: boolean, options?: StorageOptions) => Promise; + getCanAccessPremium: (options?: StorageOptions) => Promise; + getClearClipboard: (options?: StorageOptions) => Promise; + setClearClipboard: (value: number, options?: StorageOptions) => Promise; + getCollapsedGroupings: (options?: StorageOptions) => Promise; + setCollapsedGroupings: (value: string[], options?: StorageOptions) => Promise; + getConvertAccountToKeyConnector: (options?: StorageOptions) => Promise; + setConvertAccountToKeyConnector: (value: boolean, options?: StorageOptions) => Promise; + getCryptoMasterKey: (options?: StorageOptions) => Promise; + setCryptoMasterKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise; + getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise; + setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise; + getCryptoMasterKeyB64: (options?: StorageOptions) => Promise; + setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise; + getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise; + hasCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise; + setCryptoMasterKeyBiometric: (value: string, options?: StorageOptions) => Promise; + getDecodedToken: (options?: StorageOptions) => Promise; + setDecodedToken: (value: any, options?: StorageOptions) => Promise; + getDecryptedCiphers: (options?: StorageOptions) => Promise; + setDecryptedCiphers: (value: CipherView[], options?: StorageOptions) => Promise; + getDecryptedCollections: (options?: StorageOptions) => Promise; + setDecryptedCollections: (value: CollectionView[], options?: StorageOptions) => Promise; + getDecryptedCryptoSymmetricKey: (options?: StorageOptions) => Promise; + setDecryptedCryptoSymmetricKey: ( + value: SymmetricCryptoKey, + options?: StorageOptions + ) => Promise; + getDecryptedFolders: (options?: StorageOptions) => Promise; + setDecryptedFolders: (value: FolderView[], options?: StorageOptions) => Promise; + getDecryptedOrganizationKeys: ( + options?: StorageOptions + ) => Promise>; + setDecryptedOrganizationKeys: ( + value: Map, + options?: StorageOptions + ) => Promise; + getDecryptedPasswordGenerationHistory: ( + options?: StorageOptions + ) => Promise; + setDecryptedPasswordGenerationHistory: ( + value: GeneratedPasswordHistory[], + options?: StorageOptions + ) => Promise; + getDecryptedPinProtected: (options?: StorageOptions) => Promise; + setDecryptedPinProtected: (value: EncString, options?: StorageOptions) => Promise; + getDecryptedPolicies: (options?: StorageOptions) => Promise; + setDecryptedPolicies: (value: Policy[], options?: StorageOptions) => Promise; + getDecryptedPrivateKey: (options?: StorageOptions) => Promise; + setDecryptedPrivateKey: (value: ArrayBuffer, options?: StorageOptions) => Promise; + getDecryptedProviderKeys: (options?: StorageOptions) => Promise>; + setDecryptedProviderKeys: ( + value: Map, + options?: StorageOptions + ) => Promise; + getDecryptedSends: (options?: StorageOptions) => Promise; + setDecryptedSends: (value: SendView[], options?: StorageOptions) => Promise; + getDefaultUriMatch: (options?: StorageOptions) => Promise; + setDefaultUriMatch: (value: UriMatchType, options?: StorageOptions) => Promise; + getDisableAddLoginNotification: (options?: StorageOptions) => Promise; + setDisableAddLoginNotification: (value: boolean, options?: StorageOptions) => Promise; + getDisableAutoBiometricsPrompt: (options?: StorageOptions) => Promise; + setDisableAutoBiometricsPrompt: (value: boolean, options?: StorageOptions) => Promise; + getDisableAutoTotpCopy: (options?: StorageOptions) => Promise; + setDisableAutoTotpCopy: (value: boolean, options?: StorageOptions) => Promise; + getDisableBadgeCounter: (options?: StorageOptions) => Promise; + setDisableBadgeCounter: (value: boolean, options?: StorageOptions) => Promise; + getDisableChangedPasswordNotification: (options?: StorageOptions) => Promise; + setDisableChangedPasswordNotification: ( + value: boolean, + options?: StorageOptions + ) => Promise; + getDisableContextMenuItem: (options?: StorageOptions) => Promise; + setDisableContextMenuItem: (value: boolean, options?: StorageOptions) => Promise; + getDisableFavicon: (options?: StorageOptions) => Promise; + setDisableFavicon: (value: boolean, options?: StorageOptions) => Promise; + getDisableGa: (options?: StorageOptions) => Promise; + setDisableGa: (value: boolean, options?: StorageOptions) => Promise; + getDontShowCardsCurrentTab: (options?: StorageOptions) => Promise; + setDontShowCardsCurrentTab: (value: boolean, options?: StorageOptions) => Promise; + getDontShowIdentitiesCurrentTab: (options?: StorageOptions) => Promise; + setDontShowIdentitiesCurrentTab: (value: boolean, options?: StorageOptions) => Promise; + getEmail: (options?: StorageOptions) => Promise; + setEmail: (value: string, options?: StorageOptions) => Promise; + getEmailVerified: (options?: StorageOptions) => Promise; + setEmailVerified: (value: boolean, options?: StorageOptions) => Promise; + getEnableAlwaysOnTop: (options?: StorageOptions) => Promise; + setEnableAlwaysOnTop: (value: boolean, options?: StorageOptions) => Promise; + getEnableAutoFillOnPageLoad: (options?: StorageOptions) => Promise; + setEnableAutoFillOnPageLoad: (value: boolean, options?: StorageOptions) => Promise; + getEnableBiometric: (options?: StorageOptions) => Promise; + setEnableBiometric: (value: boolean, options?: StorageOptions) => Promise; + getEnableBrowserIntegration: (options?: StorageOptions) => Promise; + setEnableBrowserIntegration: (value: boolean, options?: StorageOptions) => Promise; + getEnableBrowserIntegrationFingerprint: (options?: StorageOptions) => Promise; + setEnableBrowserIntegrationFingerprint: ( + value: boolean, + options?: StorageOptions + ) => Promise; + getEnableCloseToTray: (options?: StorageOptions) => Promise; + setEnableCloseToTray: (value: boolean, options?: StorageOptions) => Promise; + getEnableFullWidth: (options?: StorageOptions) => Promise; + setEnableFullWidth: (value: boolean, options?: StorageOptions) => Promise; + getEnableGravitars: (options?: StorageOptions) => Promise; + setEnableGravitars: (value: boolean, options?: StorageOptions) => Promise; + getEnableMinimizeToTray: (options?: StorageOptions) => Promise; + setEnableMinimizeToTray: (value: boolean, options?: StorageOptions) => Promise; + getEnableStartToTray: (options?: StorageOptions) => Promise; + setEnableStartToTray: (value: boolean, options?: StorageOptions) => Promise; + getEnableTray: (options?: StorageOptions) => Promise; + setEnableTray: (value: boolean, options?: StorageOptions) => Promise; + getEncryptedCiphers: (options?: StorageOptions) => Promise<{ [id: string]: CipherData }>; + setEncryptedCiphers: ( + value: { [id: string]: CipherData }, + options?: StorageOptions + ) => Promise; + getEncryptedCollections: (options?: StorageOptions) => Promise<{ [id: string]: CollectionData }>; + setEncryptedCollections: ( + value: { [id: string]: CollectionData }, + options?: StorageOptions + ) => Promise; + getEncryptedCryptoSymmetricKey: (options?: StorageOptions) => Promise; + setEncryptedCryptoSymmetricKey: (value: string, options?: StorageOptions) => Promise; + getEncryptedFolders: (options?: StorageOptions) => Promise<{ [id: string]: FolderData }>; + setEncryptedFolders: ( + value: { [id: string]: FolderData }, + options?: StorageOptions + ) => Promise; + getEncryptedOrganizationKeys: (options?: StorageOptions) => Promise; + setEncryptedOrganizationKeys: ( + value: Map, + options?: StorageOptions + ) => Promise; + getEncryptedPasswordGenerationHistory: ( + options?: StorageOptions + ) => Promise; + setEncryptedPasswordGenerationHistory: ( + value: GeneratedPasswordHistory[], + options?: StorageOptions + ) => Promise; + getEncryptedPinProtected: (options?: StorageOptions) => Promise; + setEncryptedPinProtected: (value: string, options?: StorageOptions) => Promise; + getEncryptedPolicies: (options?: StorageOptions) => Promise<{ [id: string]: PolicyData }>; + setEncryptedPolicies: ( + value: { [id: string]: PolicyData }, + options?: StorageOptions + ) => Promise; + getEncryptedPrivateKey: (options?: StorageOptions) => Promise; + setEncryptedPrivateKey: (value: string, options?: StorageOptions) => Promise; + getEncryptedProviderKeys: (options?: StorageOptions) => Promise; + setEncryptedProviderKeys: (value: any, options?: StorageOptions) => Promise; + getEncryptedSends: (options?: StorageOptions) => Promise<{ [id: string]: SendData }>; + setEncryptedSends: (value: { [id: string]: SendData }, options?: StorageOptions) => Promise; + getEntityId: (options?: StorageOptions) => Promise; + setEntityId: (value: string, options?: StorageOptions) => Promise; + getEntityType: (options?: StorageOptions) => Promise; + setEntityType: (value: string, options?: StorageOptions) => Promise; + getEnvironmentUrls: (options?: StorageOptions) => Promise; + setEnvironmentUrls: (value: EnvironmentUrls, options?: StorageOptions) => Promise; + getEquivalentDomains: (options?: StorageOptions) => Promise; + setEquivalentDomains: (value: string, options?: StorageOptions) => Promise; + getEventCollection: (options?: StorageOptions) => Promise; + setEventCollection: (value: EventData[], options?: StorageOptions) => Promise; + getEverBeenUnlocked: (options?: StorageOptions) => Promise; + setEverBeenUnlocked: (value: boolean, options?: StorageOptions) => Promise; + getForcePasswordReset: (options?: StorageOptions) => Promise; + setForcePasswordReset: (value: boolean, options?: StorageOptions) => Promise; + getInstalledVersion: (options?: StorageOptions) => Promise; + setInstalledVersion: (value: string, options?: StorageOptions) => Promise; + getIsAuthenticated: (options?: StorageOptions) => Promise; + getKdfIterations: (options?: StorageOptions) => Promise; + setKdfIterations: (value: number, options?: StorageOptions) => Promise; + getKdfType: (options?: StorageOptions) => Promise; + setKdfType: (value: KdfType, options?: StorageOptions) => Promise; + getKeyHash: (options?: StorageOptions) => Promise; + setKeyHash: (value: string, options?: StorageOptions) => Promise; + getLastActive: (options?: StorageOptions) => Promise; + setLastActive: (value: number, options?: StorageOptions) => Promise; + getLastSync: (options?: StorageOptions) => Promise; + setLastSync: (value: string, options?: StorageOptions) => Promise; + getLegacyEtmKey: (options?: StorageOptions) => Promise; + setLegacyEtmKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise; + getLocalData: (options?: StorageOptions) => Promise; + setLocalData: (value: string, options?: StorageOptions) => Promise; + getLocale: (options?: StorageOptions) => Promise; + setLocale: (value: string, options?: StorageOptions) => Promise; + getLoginRedirect: (options?: StorageOptions) => Promise; + setLoginRedirect: (value: any, options?: StorageOptions) => Promise; + getMainWindowSize: (options?: StorageOptions) => Promise; + setMainWindowSize: (value: number, options?: StorageOptions) => Promise; + getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise; + setMinimizeOnCopyToClipboard: (value: boolean, options?: StorageOptions) => Promise; + getNeverDomains: (options?: StorageOptions) => Promise<{ [id: string]: any }>; + setNeverDomains: (value: { [id: string]: any }, options?: StorageOptions) => Promise; + getNoAutoPromptBiometrics: (options?: StorageOptions) => Promise; + setNoAutoPromptBiometrics: (value: boolean, options?: StorageOptions) => Promise; + getNoAutoPromptBiometricsText: (options?: StorageOptions) => Promise; + setNoAutoPromptBiometricsText: (value: string, options?: StorageOptions) => Promise; + getOpenAtLogin: (options?: StorageOptions) => Promise; + setOpenAtLogin: (value: boolean, options?: StorageOptions) => Promise; + getOrganizationInvitation: (options?: StorageOptions) => Promise; + setOrganizationInvitation: (value: any, options?: StorageOptions) => Promise; + getOrganizations: (options?: StorageOptions) => Promise<{ [id: string]: OrganizationData }>; + setOrganizations: ( + value: { [id: string]: OrganizationData }, + options?: StorageOptions + ) => Promise; + getPasswordGenerationOptions: (options?: StorageOptions) => Promise; + setPasswordGenerationOptions: (value: any, options?: StorageOptions) => Promise; + getUsernameGenerationOptions: (options?: StorageOptions) => Promise; + setUsernameGenerationOptions: (value: any, options?: StorageOptions) => Promise; + getGeneratorOptions: (options?: StorageOptions) => Promise; + setGeneratorOptions: (value: any, options?: StorageOptions) => Promise; + getProtectedPin: (options?: StorageOptions) => Promise; + setProtectedPin: (value: string, options?: StorageOptions) => Promise; + getProviders: (options?: StorageOptions) => Promise<{ [id: string]: ProviderData }>; + setProviders: (value: { [id: string]: ProviderData }, options?: StorageOptions) => Promise; + getPublicKey: (options?: StorageOptions) => Promise; + setPublicKey: (value: ArrayBuffer, options?: StorageOptions) => Promise; + getRefreshToken: (options?: StorageOptions) => Promise; + setRefreshToken: (value: string, options?: StorageOptions) => Promise; + getRememberedEmail: (options?: StorageOptions) => Promise; + setRememberedEmail: (value: string, options?: StorageOptions) => Promise; + getSecurityStamp: (options?: StorageOptions) => Promise; + setSecurityStamp: (value: string, options?: StorageOptions) => Promise; + getSettings: (options?: StorageOptions) => Promise; + setSettings: (value: string, options?: StorageOptions) => Promise; + getSsoCodeVerifier: (options?: StorageOptions) => Promise; + setSsoCodeVerifier: (value: string, options?: StorageOptions) => Promise; + getSsoOrgIdentifier: (options?: StorageOptions) => Promise; + setSsoOrganizationIdentifier: (value: string, options?: StorageOptions) => Promise; + getSsoState: (options?: StorageOptions) => Promise; + setSsoState: (value: string, options?: StorageOptions) => Promise; + getTheme: (options?: StorageOptions) => Promise; + setTheme: (value: ThemeType, options?: StorageOptions) => Promise; + getTwoFactorToken: (options?: StorageOptions) => Promise; + setTwoFactorToken: (value: string, options?: StorageOptions) => Promise; + getUserId: (options?: StorageOptions) => Promise; + getUsesKeyConnector: (options?: StorageOptions) => Promise; + setUsesKeyConnector: (vaule: boolean, options?: StorageOptions) => Promise; + getVaultTimeout: (options?: StorageOptions) => Promise; + setVaultTimeout: (value: number, options?: StorageOptions) => Promise; + getVaultTimeoutAction: (options?: StorageOptions) => Promise; + setVaultTimeoutAction: (value: string, options?: StorageOptions) => Promise; + getStateVersion: () => Promise; + setStateVersion: (value: number) => Promise; + getWindow: () => Promise; + setWindow: (value: WindowState) => Promise; +} diff --git a/jslib/common/src/abstractions/stateMigration.service.ts b/jslib/common/src/abstractions/stateMigration.service.ts new file mode 100644 index 00000000..f16777a1 --- /dev/null +++ b/jslib/common/src/abstractions/stateMigration.service.ts @@ -0,0 +1,4 @@ +export abstract class StateMigrationService { + needsMigration: () => Promise; + migrate: () => Promise; +} diff --git a/jslib/common/src/abstractions/storage.service.ts b/jslib/common/src/abstractions/storage.service.ts new file mode 100644 index 00000000..f522d3cf --- /dev/null +++ b/jslib/common/src/abstractions/storage.service.ts @@ -0,0 +1,8 @@ +import { StorageOptions } from "../models/domain/storageOptions"; + +export abstract class StorageService { + get: (key: string, options?: StorageOptions) => Promise; + has: (key: string, options?: StorageOptions) => Promise; + save: (key: string, obj: any, options?: StorageOptions) => Promise; + remove: (key: string, options?: StorageOptions) => Promise; +} diff --git a/jslib/common/src/abstractions/sync.service.ts b/jslib/common/src/abstractions/sync.service.ts new file mode 100644 index 00000000..936d027f --- /dev/null +++ b/jslib/common/src/abstractions/sync.service.ts @@ -0,0 +1,19 @@ +import { + SyncCipherNotification, + SyncFolderNotification, + SyncSendNotification, +} from "../models/response/notificationResponse"; + +export abstract class SyncService { + syncInProgress: boolean; + + getLastSync: () => Promise; + setLastSync: (date: Date, userId?: string) => Promise; + fullSync: (forceSync: boolean, allowThrowOnError?: boolean) => Promise; + syncUpsertFolder: (notification: SyncFolderNotification, isEdit: boolean) => Promise; + syncDeleteFolder: (notification: SyncFolderNotification) => Promise; + syncUpsertCipher: (notification: SyncCipherNotification, isEdit: boolean) => Promise; + syncDeleteCipher: (notification: SyncFolderNotification) => Promise; + syncUpsertSend: (notification: SyncSendNotification, isEdit: boolean) => Promise; + syncDeleteSend: (notification: SyncSendNotification) => Promise; +} diff --git a/jslib/common/src/abstractions/system.service.ts b/jslib/common/src/abstractions/system.service.ts new file mode 100644 index 00000000..b5c1e595 --- /dev/null +++ b/jslib/common/src/abstractions/system.service.ts @@ -0,0 +1,6 @@ +export abstract class SystemService { + startProcessReload: () => Promise; + cancelProcessReload: () => void; + clearClipboard: (clipboardValue: string, timeoutMs?: number) => Promise; + clearPendingClipboard: () => Promise; +} diff --git a/jslib/common/src/abstractions/token.service.ts b/jslib/common/src/abstractions/token.service.ts new file mode 100644 index 00000000..f0470494 --- /dev/null +++ b/jslib/common/src/abstractions/token.service.ts @@ -0,0 +1,32 @@ +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; + +export abstract class TokenService { + setTokens: ( + accessToken: string, + refreshToken: string, + clientIdClientSecret: [string, string] + ) => Promise; + setToken: (token: string) => Promise; + getToken: () => Promise; + setRefreshToken: (refreshToken: string) => Promise; + getRefreshToken: () => Promise; + setClientId: (clientId: string) => Promise; + getClientId: () => Promise; + setClientSecret: (clientSecret: string) => Promise; + getClientSecret: () => Promise; + setTwoFactorToken: (tokenResponse: IdentityTokenResponse) => Promise; + getTwoFactorToken: () => Promise; + clearTwoFactorToken: () => Promise; + clearToken: (userId?: string) => Promise; + decodeToken: (token?: string) => any; + getTokenExpirationDate: () => Promise; + tokenSecondsRemaining: (offsetSeconds?: number) => Promise; + tokenNeedsRefresh: (minutes?: number) => Promise; + getUserId: () => Promise; + getEmail: () => Promise; + getEmailVerified: () => Promise; + getName: () => Promise; + getPremium: () => Promise; + getIssuer: () => Promise; + getIsExternal: () => Promise; +} diff --git a/jslib/common/src/abstractions/totp.service.ts b/jslib/common/src/abstractions/totp.service.ts new file mode 100644 index 00000000..bf143e26 --- /dev/null +++ b/jslib/common/src/abstractions/totp.service.ts @@ -0,0 +1,5 @@ +export abstract class TotpService { + getCode: (key: string) => Promise; + getTimeInterval: (key: string) => number; + isAutoCopyEnabled: () => Promise; +} diff --git a/jslib/common/src/abstractions/twoFactor.service.ts b/jslib/common/src/abstractions/twoFactor.service.ts new file mode 100644 index 00000000..74f09167 --- /dev/null +++ b/jslib/common/src/abstractions/twoFactor.service.ts @@ -0,0 +1,23 @@ +import { TwoFactorProviderType } from "../enums/twoFactorProviderType"; +import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; + +export interface TwoFactorProviderDetails { + type: TwoFactorProviderType; + name: string; + description: string; + priority: number; + sort: number; + premium: boolean; +} + +export abstract class TwoFactorService { + init: () => void; + getSupportedProviders: (win: Window) => TwoFactorProviderDetails[]; + getDefaultProvider: (webAuthnSupported: boolean) => TwoFactorProviderType; + setSelectedProvider: (type: TwoFactorProviderType) => void; + clearSelectedProvider: () => void; + + setProviders: (response: IdentityTwoFactorResponse) => void; + clearProviders: () => void; + getProviders: () => Map; +} diff --git a/jslib/common/src/abstractions/userVerification.service.ts b/jslib/common/src/abstractions/userVerification.service.ts new file mode 100644 index 00000000..ebd7d1f3 --- /dev/null +++ b/jslib/common/src/abstractions/userVerification.service.ts @@ -0,0 +1,12 @@ +import { SecretVerificationRequest } from "../models/request/secretVerificationRequest"; +import { Verification } from "../types/verification"; + +export abstract class UserVerificationService { + buildRequest: ( + verification: Verification, + requestClass?: new () => T, + alreadyHashed?: boolean + ) => Promise; + verifyUser: (verification: Verification) => Promise; + requestOTP: () => Promise; +} diff --git a/jslib/common/src/abstractions/usernameGeneration.service.ts b/jslib/common/src/abstractions/usernameGeneration.service.ts new file mode 100644 index 00000000..baba74d8 --- /dev/null +++ b/jslib/common/src/abstractions/usernameGeneration.service.ts @@ -0,0 +1,8 @@ +export abstract class UsernameGenerationService { + generateUsername: (options: any) => Promise; + generateWord: (options: any) => Promise; + generateSubaddress: (options: any) => Promise; + generateCatchall: (options: any) => Promise; + getOptions: () => Promise; + saveOptions: (options: any) => Promise; +} diff --git a/jslib/common/src/abstractions/vaultTimeout.service.ts b/jslib/common/src/abstractions/vaultTimeout.service.ts new file mode 100644 index 00000000..2c2b2521 --- /dev/null +++ b/jslib/common/src/abstractions/vaultTimeout.service.ts @@ -0,0 +1,11 @@ +export abstract class VaultTimeoutService { + isLocked: (userId?: string) => Promise; + checkVaultTimeout: () => Promise; + lock: (allowSoftLock?: boolean, userId?: string) => Promise; + logOut: (userId?: string) => Promise; + setVaultTimeoutOptions: (vaultTimeout: number, vaultTimeoutAction: string) => Promise; + getVaultTimeout: () => Promise; + isPinLockSet: () => Promise<[boolean, boolean]>; + isBiometricLockSet: () => Promise; + clear: (userId?: string) => Promise; +} diff --git a/jslib/common/src/enums/authenticationStatus.ts b/jslib/common/src/enums/authenticationStatus.ts new file mode 100644 index 00000000..8e1db548 --- /dev/null +++ b/jslib/common/src/enums/authenticationStatus.ts @@ -0,0 +1,6 @@ +export enum AuthenticationStatus { + Locked = "locked", + Unlocked = "unlocked", + LoggedOut = "loggedOut", + Active = "active", +} diff --git a/jslib/common/src/enums/authenticationType.ts b/jslib/common/src/enums/authenticationType.ts new file mode 100644 index 00000000..ed7375c8 --- /dev/null +++ b/jslib/common/src/enums/authenticationType.ts @@ -0,0 +1,5 @@ +export enum AuthenticationType { + Password = 0, + Sso = 1, + Api = 2, +} diff --git a/jslib/common/src/enums/cipherRepromptType.ts b/jslib/common/src/enums/cipherRepromptType.ts new file mode 100644 index 00000000..1d0a523c --- /dev/null +++ b/jslib/common/src/enums/cipherRepromptType.ts @@ -0,0 +1,4 @@ +export enum CipherRepromptType { + None = 0, + Password = 1, +} diff --git a/jslib/common/src/enums/cipherType.ts b/jslib/common/src/enums/cipherType.ts new file mode 100644 index 00000000..cce7874d --- /dev/null +++ b/jslib/common/src/enums/cipherType.ts @@ -0,0 +1,6 @@ +export enum CipherType { + Login = 1, + SecureNote = 2, + Card = 3, + Identity = 4, +} diff --git a/jslib/common/src/enums/clientType.ts b/jslib/common/src/enums/clientType.ts new file mode 100644 index 00000000..246769eb --- /dev/null +++ b/jslib/common/src/enums/clientType.ts @@ -0,0 +1,8 @@ +export enum ClientType { + Web = "web", + Browser = "browser", + Desktop = "desktop", + Mobile = "mobile", + Cli = "cli", + DirectoryConnector = "connector", +} diff --git a/jslib/common/src/enums/deviceType.ts b/jslib/common/src/enums/deviceType.ts new file mode 100644 index 00000000..707f83d8 --- /dev/null +++ b/jslib/common/src/enums/deviceType.ts @@ -0,0 +1,23 @@ +export enum DeviceType { + Android = 0, + iOS = 1, + ChromeExtension = 2, + FirefoxExtension = 3, + OperaExtension = 4, + EdgeExtension = 5, + WindowsDesktop = 6, + MacOsDesktop = 7, + LinuxDesktop = 8, + ChromeBrowser = 9, + FirefoxBrowser = 10, + OperaBrowser = 11, + EdgeBrowser = 12, + IEBrowser = 13, + UnknownBrowser = 14, + AndroidAmazon = 15, + UWP = 16, + SafariBrowser = 17, + VivaldiBrowser = 18, + VivaldiExtension = 19, + SafariExtension = 20, +} diff --git a/jslib/common/src/enums/emergencyAccessStatusType.ts b/jslib/common/src/enums/emergencyAccessStatusType.ts new file mode 100644 index 00000000..94400f34 --- /dev/null +++ b/jslib/common/src/enums/emergencyAccessStatusType.ts @@ -0,0 +1,7 @@ +export enum EmergencyAccessStatusType { + Invited = 0, + Accepted = 1, + Confirmed = 2, + RecoveryInitiated = 3, + RecoveryApproved = 4, +} diff --git a/jslib/common/src/enums/emergencyAccessType.ts b/jslib/common/src/enums/emergencyAccessType.ts new file mode 100644 index 00000000..61a366c4 --- /dev/null +++ b/jslib/common/src/enums/emergencyAccessType.ts @@ -0,0 +1,4 @@ +export enum EmergencyAccessType { + View = 0, + Takeover = 1, +} diff --git a/jslib/common/src/enums/encryptionType.ts b/jslib/common/src/enums/encryptionType.ts new file mode 100644 index 00000000..1c12dba2 --- /dev/null +++ b/jslib/common/src/enums/encryptionType.ts @@ -0,0 +1,9 @@ +export enum EncryptionType { + AesCbc256_B64 = 0, + AesCbc128_HmacSha256_B64 = 1, + AesCbc256_HmacSha256_B64 = 2, + Rsa2048_OaepSha256_B64 = 3, + Rsa2048_OaepSha1_B64 = 4, + Rsa2048_OaepSha256_HmacSha256_B64 = 5, + Rsa2048_OaepSha1_HmacSha256_B64 = 6, +} diff --git a/jslib/common/src/enums/eventType.ts b/jslib/common/src/enums/eventType.ts new file mode 100644 index 00000000..236e73c0 --- /dev/null +++ b/jslib/common/src/enums/eventType.ts @@ -0,0 +1,72 @@ +export enum EventType { + User_LoggedIn = 1000, + User_ChangedPassword = 1001, + User_Updated2fa = 1002, + User_Disabled2fa = 1003, + User_Recovered2fa = 1004, + User_FailedLogIn = 1005, + User_FailedLogIn2fa = 1006, + User_ClientExportedVault = 1007, + User_UpdatedTempPassword = 1008, + User_MigratedKeyToKeyConnector = 1009, + + Cipher_Created = 1100, + Cipher_Updated = 1101, + Cipher_Deleted = 1102, + Cipher_AttachmentCreated = 1103, + Cipher_AttachmentDeleted = 1104, + Cipher_Shared = 1105, + Cipher_UpdatedCollections = 1106, + Cipher_ClientViewed = 1107, + Cipher_ClientToggledPasswordVisible = 1108, + Cipher_ClientToggledHiddenFieldVisible = 1109, + Cipher_ClientToggledCardCodeVisible = 1110, + Cipher_ClientCopiedPassword = 1111, + Cipher_ClientCopiedHiddenField = 1112, + Cipher_ClientCopiedCardCode = 1113, + Cipher_ClientAutofilled = 1114, + Cipher_SoftDeleted = 1115, + Cipher_Restored = 1116, + Cipher_ClientToggledCardNumberVisible = 1117, + + Collection_Created = 1300, + Collection_Updated = 1301, + Collection_Deleted = 1302, + + Group_Created = 1400, + Group_Updated = 1401, + Group_Deleted = 1402, + + OrganizationUser_Invited = 1500, + OrganizationUser_Confirmed = 1501, + OrganizationUser_Updated = 1502, + OrganizationUser_Removed = 1503, + OrganizationUser_UpdatedGroups = 1504, + OrganizationUser_UnlinkedSso = 1505, + OrganizationUser_ResetPassword_Enroll = 1506, + OrganizationUser_ResetPassword_Withdraw = 1507, + OrganizationUser_AdminResetPassword = 1508, + OrganizationUser_ResetSsoLink = 1509, + OrganizationUser_FirstSsoLogin = 1510, + + Organization_Updated = 1600, + Organization_PurgedVault = 1601, + // Organization_ClientExportedVault = 1602, + Organization_VaultAccessed = 1603, + Organization_EnabledSso = 1604, + Organization_DisabledSso = 1605, + Organization_EnabledKeyConnector = 1606, + Organization_DisabledKeyConnector = 1607, + + Policy_Updated = 1700, + + ProviderUser_Invited = 1800, + ProviderUser_Confirmed = 1801, + ProviderUser_Updated = 1802, + ProviderUser_Removed = 1803, + + ProviderOrganization_Created = 1900, + ProviderOrganization_Added = 1901, + ProviderOrganization_Removed = 1902, + ProviderOrganization_VaultAccessed = 1903, +} diff --git a/jslib/common/src/enums/fieldType.ts b/jslib/common/src/enums/fieldType.ts new file mode 100644 index 00000000..d6deb30e --- /dev/null +++ b/jslib/common/src/enums/fieldType.ts @@ -0,0 +1,6 @@ +export enum FieldType { + Text = 0, + Hidden = 1, + Boolean = 2, + Linked = 3, +} diff --git a/jslib/common/src/enums/fileUploadType.ts b/jslib/common/src/enums/fileUploadType.ts new file mode 100644 index 00000000..4cf654fd --- /dev/null +++ b/jslib/common/src/enums/fileUploadType.ts @@ -0,0 +1,4 @@ +export enum FileUploadType { + Direct = 0, + Azure = 1, +} diff --git a/jslib/common/src/enums/hashPurpose.ts b/jslib/common/src/enums/hashPurpose.ts new file mode 100644 index 00000000..887931b9 --- /dev/null +++ b/jslib/common/src/enums/hashPurpose.ts @@ -0,0 +1,4 @@ +export enum HashPurpose { + ServerAuthorization = 1, + LocalAuthorization = 2, +} diff --git a/jslib/common/src/enums/htmlStorageLocation.ts b/jslib/common/src/enums/htmlStorageLocation.ts new file mode 100644 index 00000000..19c84fe8 --- /dev/null +++ b/jslib/common/src/enums/htmlStorageLocation.ts @@ -0,0 +1,5 @@ +export enum HtmlStorageLocation { + Local = "local", + Memory = "memory", + Session = "session", +} diff --git a/jslib/common/src/enums/importOptions.ts b/jslib/common/src/enums/importOptions.ts new file mode 100644 index 00000000..6535b133 --- /dev/null +++ b/jslib/common/src/enums/importOptions.ts @@ -0,0 +1,74 @@ +export interface ImportOption { + id: string; + name: string; +} + +export const featuredImportOptions = [ + { id: "bitwardenjson", name: "Bitwarden (json)" }, + { id: "bitwardencsv", name: "Bitwarden (csv)" }, + { id: "chromecsv", name: "Chrome (csv)" }, + { id: "dashlanecsv", name: "Dashlane (csv)" }, + { id: "firefoxcsv", name: "Firefox (csv)" }, + { id: "keepass2xml", name: "KeePass 2 (xml)" }, + { id: "lastpasscsv", name: "LastPass (csv)" }, + { id: "safaricsv", name: "Safari and macOS (csv)" }, + { id: "1password1pux", name: "1Password (1pux)" }, +] as const; + +export const regularImportOptions = [ + { id: "keepassxcsv", name: "KeePassX (csv)" }, + { id: "1password1pif", name: "1Password (1pif)" }, + { id: "1passwordwincsv", name: "1Password 6 and 7 Windows (csv)" }, + { id: "1passwordmaccsv", name: "1Password 6 and 7 Mac (csv)" }, + { id: "dashlanejson", name: "Dashlane (json)" }, + { id: "roboformcsv", name: "RoboForm (csv)" }, + { id: "keepercsv", name: "Keeper (csv)" }, + // Temporarily remove this option for the Feb release + // { id: "keeperjson", name: "Keeper (json)" }, + { id: "enpasscsv", name: "Enpass (csv)" }, + { id: "enpassjson", name: "Enpass (json)" }, + { id: "safeincloudxml", name: "SafeInCloud (xml)" }, + { id: "pwsafexml", name: "Password Safe (xml)" }, + { id: "stickypasswordxml", name: "Sticky Password (xml)" }, + { id: "msecurecsv", name: "mSecure (csv)" }, + { id: "truekeycsv", name: "True Key (csv)" }, + { id: "passwordbossjson", name: "Password Boss (json)" }, + { id: "zohovaultcsv", name: "Zoho Vault (csv)" }, + { id: "splashidcsv", name: "SplashID (csv)" }, + { id: "passworddragonxml", name: "Password Dragon (xml)" }, + { id: "padlockcsv", name: "Padlock (csv)" }, + { id: "passboltcsv", name: "Passbolt (csv)" }, + { id: "clipperzhtml", name: "Clipperz (html)" }, + { id: "aviracsv", name: "Avira (csv)" }, + { id: "saferpasscsv", name: "SaferPass (csv)" }, + { id: "upmcsv", name: "Universal Password Manager (csv)" }, + { id: "ascendocsv", name: "Ascendo DataVault (csv)" }, + { id: "meldiumcsv", name: "Meldium (csv)" }, + { id: "passkeepcsv", name: "PassKeep (csv)" }, + { id: "operacsv", name: "Opera (csv)" }, + { id: "vivaldicsv", name: "Vivaldi (csv)" }, + { id: "gnomejson", name: "GNOME Passwords and Keys/Seahorse (json)" }, + { id: "blurcsv", name: "Blur (csv)" }, + { id: "passwordagentcsv", name: "Password Agent (csv)" }, + { id: "passpackcsv", name: "Passpack (csv)" }, + { id: "passmanjson", name: "Passman (json)" }, + { id: "avastcsv", name: "Avast Passwords (csv)" }, + { id: "avastjson", name: "Avast Passwords (json)" }, + { id: "fsecurefsk", name: "F-Secure KEY (fsk)" }, + { id: "kasperskytxt", name: "Kaspersky Password Manager (txt)" }, + { id: "remembearcsv", name: "RememBear (csv)" }, + { id: "passwordwallettxt", name: "PasswordWallet (txt)" }, + { id: "mykicsv", name: "Myki (csv)" }, + { id: "securesafecsv", name: "SecureSafe (csv)" }, + { id: "logmeoncecsv", name: "LogMeOnce (csv)" }, + { id: "blackberrycsv", name: "BlackBerry Password Keeper (csv)" }, + { id: "buttercupcsv", name: "Buttercup (csv)" }, + { id: "codebookcsv", name: "Codebook (csv)" }, + { id: "encryptrcsv", name: "Encryptr (csv)" }, + { id: "yoticsv", name: "Yoti (csv)" }, + { id: "nordpasscsv", name: "Nordpass (csv)" }, +] as const; + +export type ImportType = + | typeof featuredImportOptions[number]["id"] + | typeof regularImportOptions[number]["id"]; diff --git a/jslib/common/src/enums/kdfType.ts b/jslib/common/src/enums/kdfType.ts new file mode 100644 index 00000000..cc7fa7e0 --- /dev/null +++ b/jslib/common/src/enums/kdfType.ts @@ -0,0 +1,7 @@ +export enum KdfType { + PBKDF2_SHA256 = 0, +} + +export const DEFAULT_KDF_TYPE = KdfType.PBKDF2_SHA256; +export const DEFAULT_KDF_ITERATIONS = 100000; +export const SEND_KDF_ITERATIONS = 100000; diff --git a/jslib/common/src/enums/keySuffixOptions.ts b/jslib/common/src/enums/keySuffixOptions.ts new file mode 100644 index 00000000..2ae98d8e --- /dev/null +++ b/jslib/common/src/enums/keySuffixOptions.ts @@ -0,0 +1,4 @@ +export enum KeySuffixOptions { + Auto = "auto", + Biometric = "biometric", +} diff --git a/jslib/common/src/enums/linkedIdType.ts b/jslib/common/src/enums/linkedIdType.ts new file mode 100644 index 00000000..c38ebc1c --- /dev/null +++ b/jslib/common/src/enums/linkedIdType.ts @@ -0,0 +1,40 @@ +export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId; + +// LoginView +export enum LoginLinkedId { + Username = 100, + Password = 101, +} + +// CardView +export enum CardLinkedId { + CardholderName = 300, + ExpMonth = 301, + ExpYear = 302, + Code = 303, + Brand = 304, + Number = 305, +} + +// IdentityView +export enum IdentityLinkedId { + Title = 400, + MiddleName = 401, + Address1 = 402, + Address2 = 403, + Address3 = 404, + City = 405, + State = 406, + PostalCode = 407, + Country = 408, + Company = 409, + Email = 410, + Phone = 411, + Ssn = 412, + Username = 413, + PassportNumber = 414, + LicenseNumber = 415, + FirstName = 416, + LastName = 417, + FullName = 418, +} diff --git a/jslib/common/src/enums/logLevelType.ts b/jslib/common/src/enums/logLevelType.ts new file mode 100644 index 00000000..709871dd --- /dev/null +++ b/jslib/common/src/enums/logLevelType.ts @@ -0,0 +1,6 @@ +export enum LogLevelType { + Debug, + Info, + Warning, + Error, +} diff --git a/jslib/common/src/enums/notificationType.ts b/jslib/common/src/enums/notificationType.ts new file mode 100644 index 00000000..77ebde01 --- /dev/null +++ b/jslib/common/src/enums/notificationType.ts @@ -0,0 +1,20 @@ +export enum NotificationType { + SyncCipherUpdate = 0, + SyncCipherCreate = 1, + SyncLoginDelete = 2, + SyncFolderDelete = 3, + SyncCiphers = 4, + + SyncVault = 5, + SyncOrgKeys = 6, + SyncFolderCreate = 7, + SyncFolderUpdate = 8, + SyncCipherDelete = 9, + SyncSettings = 10, + + LogOut = 11, + + SyncSendCreate = 12, + SyncSendUpdate = 13, + SyncSendDelete = 14, +} diff --git a/jslib/common/src/enums/organizationUserStatusType.ts b/jslib/common/src/enums/organizationUserStatusType.ts new file mode 100644 index 00000000..33422180 --- /dev/null +++ b/jslib/common/src/enums/organizationUserStatusType.ts @@ -0,0 +1,5 @@ +export enum OrganizationUserStatusType { + Invited = 0, + Accepted = 1, + Confirmed = 2, +} diff --git a/jslib/common/src/enums/organizationUserType.ts b/jslib/common/src/enums/organizationUserType.ts new file mode 100644 index 00000000..950fbaae --- /dev/null +++ b/jslib/common/src/enums/organizationUserType.ts @@ -0,0 +1,7 @@ +export enum OrganizationUserType { + Owner = 0, + Admin = 1, + User = 2, + Manager = 3, + Custom = 4, +} diff --git a/jslib/common/src/enums/paymentMethodType.ts b/jslib/common/src/enums/paymentMethodType.ts new file mode 100644 index 00000000..701bd886 --- /dev/null +++ b/jslib/common/src/enums/paymentMethodType.ts @@ -0,0 +1,11 @@ +export enum PaymentMethodType { + Card = 0, + BankAccount = 1, + PayPal = 2, + BitPay = 3, + Credit = 4, + WireTransfer = 5, + AppleInApp = 6, + GoogleInApp = 7, + Check = 8, +} diff --git a/jslib/common/src/enums/permissions.ts b/jslib/common/src/enums/permissions.ts new file mode 100644 index 00000000..46a74455 --- /dev/null +++ b/jslib/common/src/enums/permissions.ts @@ -0,0 +1,27 @@ +export enum Permissions { + AccessEventLogs, + AccessImportExport, + AccessReports, + /** + * @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and + * `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0 + */ + ManageAllCollections, + /** + * @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and + * `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0 + */ + ManageAssignedCollections, + ManageGroups, + ManageOrganization, + ManagePolicies, + ManageProvider, + ManageUsers, + ManageUsersPassword, + CreateNewCollections, + EditAnyCollection, + DeleteAnyCollection, + EditAssignedCollections, + DeleteAssignedCollections, + ManageSso, +} diff --git a/jslib/common/src/enums/planSponsorshipType.ts b/jslib/common/src/enums/planSponsorshipType.ts new file mode 100644 index 00000000..3b4c0046 --- /dev/null +++ b/jslib/common/src/enums/planSponsorshipType.ts @@ -0,0 +1,3 @@ +export enum PlanSponsorshipType { + FamiliesForEnterprise = 0, +} diff --git a/jslib/common/src/enums/planType.ts b/jslib/common/src/enums/planType.ts new file mode 100644 index 00000000..f4c89d2c --- /dev/null +++ b/jslib/common/src/enums/planType.ts @@ -0,0 +1,14 @@ +export enum PlanType { + Free = 0, + FamiliesAnnually2019 = 1, + TeamsMonthly2019 = 2, + TeamsAnnually2019 = 3, + EnterpriseMonthly2019 = 4, + EnterpriseAnnually2019 = 5, + Custom = 6, + FamiliesAnnually = 7, + TeamsMonthly = 8, + TeamsAnnually = 9, + EnterpriseMonthly = 10, + EnterpriseAnnually = 11, +} diff --git a/jslib/common/src/enums/policyType.ts b/jslib/common/src/enums/policyType.ts new file mode 100644 index 00000000..02dce41e --- /dev/null +++ b/jslib/common/src/enums/policyType.ts @@ -0,0 +1,13 @@ +export enum PolicyType { + TwoFactorAuthentication = 0, // Requires users to have 2fa enabled + MasterPassword = 1, // Sets minimum requirements for master password complexity + PasswordGenerator = 2, // Sets minimum requirements/default type for generated passwords/passphrases + SingleOrg = 3, // Allows users to only be apart of one organization + RequireSso = 4, // Requires users to authenticate with SSO + PersonalOwnership = 5, // Disables personal vault ownership for adding/cloning items + DisableSend = 6, // Disables the ability to create and edit Bitwarden Sends + SendOptions = 7, // Sets restrictions or defaults for Bitwarden Sends + ResetPassword = 8, // Allows orgs to use reset password : also can enable auto-enrollment during invite flow + MaximumVaultTimeout = 9, // Sets the maximum allowed vault timeout + DisablePersonalVaultExport = 10, // Disable personal vault export +} diff --git a/jslib/common/src/enums/productType.ts b/jslib/common/src/enums/productType.ts new file mode 100644 index 00000000..50b836d1 --- /dev/null +++ b/jslib/common/src/enums/productType.ts @@ -0,0 +1,6 @@ +export enum ProductType { + Free = 0, + Families = 1, + Teams = 2, + Enterprise = 3, +} diff --git a/jslib/common/src/enums/providerUserStatusType.ts b/jslib/common/src/enums/providerUserStatusType.ts new file mode 100644 index 00000000..3b845551 --- /dev/null +++ b/jslib/common/src/enums/providerUserStatusType.ts @@ -0,0 +1,5 @@ +export enum ProviderUserStatusType { + Invited = 0, + Accepted = 1, + Confirmed = 2, +} diff --git a/jslib/common/src/enums/providerUserType.ts b/jslib/common/src/enums/providerUserType.ts new file mode 100644 index 00000000..00490adc --- /dev/null +++ b/jslib/common/src/enums/providerUserType.ts @@ -0,0 +1,4 @@ +export enum ProviderUserType { + ProviderAdmin = 0, + ServiceUser = 1, +} diff --git a/jslib/common/src/enums/secureNoteType.ts b/jslib/common/src/enums/secureNoteType.ts new file mode 100644 index 00000000..8015236d --- /dev/null +++ b/jslib/common/src/enums/secureNoteType.ts @@ -0,0 +1,3 @@ +export enum SecureNoteType { + Generic = 0, +} diff --git a/jslib/common/src/enums/sendType.ts b/jslib/common/src/enums/sendType.ts new file mode 100644 index 00000000..487930c9 --- /dev/null +++ b/jslib/common/src/enums/sendType.ts @@ -0,0 +1,4 @@ +export enum SendType { + Text = 0, + File = 1, +} diff --git a/jslib/common/src/enums/ssoEnums.ts b/jslib/common/src/enums/ssoEnums.ts new file mode 100644 index 00000000..d9be7274 --- /dev/null +++ b/jslib/common/src/enums/ssoEnums.ts @@ -0,0 +1,33 @@ +export enum SsoType { + None = 0, + OpenIdConnect = 1, + Saml2 = 2, +} + +export enum OpenIdConnectRedirectBehavior { + RedirectGet = 0, + FormPost = 1, +} + +export enum Saml2BindingType { + HttpRedirect = 1, + HttpPost = 2, +} + +export enum Saml2NameIdFormat { + NotConfigured = 0, + Unspecified = 1, + EmailAddress = 2, + X509SubjectName = 3, + WindowsDomainQualifiedName = 4, + KerberosPrincipalName = 5, + EntityIdentifier = 6, + Persistent = 7, + Transient = 8, +} + +export enum Saml2SigningBehavior { + IfIdpWantAuthnRequestsSigned = 0, + Always = 1, + Never = 3, +} diff --git a/jslib/common/src/enums/stateVersion.ts b/jslib/common/src/enums/stateVersion.ts new file mode 100644 index 00000000..5aeb02e5 --- /dev/null +++ b/jslib/common/src/enums/stateVersion.ts @@ -0,0 +1,7 @@ +export enum StateVersion { + One = 1, // Original flat key/value pair store + Two = 2, // Move to a typed State object + Three = 3, // Fix migration of users' premium status + Four = 4, // Fix 'Never Lock' option by removing stale data + Latest = Four, +} diff --git a/jslib/common/src/enums/storageLocation.ts b/jslib/common/src/enums/storageLocation.ts new file mode 100644 index 00000000..46d50d1d --- /dev/null +++ b/jslib/common/src/enums/storageLocation.ts @@ -0,0 +1,5 @@ +export enum StorageLocation { + Both = "both", + Disk = "disk", + Memory = "memory", +} diff --git a/jslib/common/src/enums/themeType.ts b/jslib/common/src/enums/themeType.ts new file mode 100644 index 00000000..8afca770 --- /dev/null +++ b/jslib/common/src/enums/themeType.ts @@ -0,0 +1,7 @@ +export enum ThemeType { + System = "system", + Light = "light", + Dark = "dark", + Nord = "nord", + SolarizedDark = "solarizedDark", +} diff --git a/jslib/common/src/enums/transactionType.ts b/jslib/common/src/enums/transactionType.ts new file mode 100644 index 00000000..34731a2e --- /dev/null +++ b/jslib/common/src/enums/transactionType.ts @@ -0,0 +1,7 @@ +export enum TransactionType { + Charge = 0, + Credit = 1, + PromotionalCredit = 2, + ReferralCredit = 3, + Refund = 4, +} diff --git a/jslib/common/src/enums/twoFactorProviderType.ts b/jslib/common/src/enums/twoFactorProviderType.ts new file mode 100644 index 00000000..a1708032 --- /dev/null +++ b/jslib/common/src/enums/twoFactorProviderType.ts @@ -0,0 +1,10 @@ +export enum TwoFactorProviderType { + Authenticator = 0, + Email = 1, + Duo = 2, + Yubikey = 3, + U2f = 4, + Remember = 5, + OrganizationDuo = 6, + WebAuthn = 7, +} diff --git a/jslib/common/src/enums/uriMatchType.ts b/jslib/common/src/enums/uriMatchType.ts new file mode 100644 index 00000000..4a4193ba --- /dev/null +++ b/jslib/common/src/enums/uriMatchType.ts @@ -0,0 +1,8 @@ +export enum UriMatchType { + Domain = 0, + Host = 1, + StartsWith = 2, + Exact = 3, + RegularExpression = 4, + Never = 5, +} diff --git a/jslib/common/src/enums/verificationType.ts b/jslib/common/src/enums/verificationType.ts new file mode 100644 index 00000000..76a51ab7 --- /dev/null +++ b/jslib/common/src/enums/verificationType.ts @@ -0,0 +1,4 @@ +export enum VerificationType { + MasterPassword = 0, + OTP = 1, +} diff --git a/jslib/common/src/factories/accountFactory.ts b/jslib/common/src/factories/accountFactory.ts new file mode 100644 index 00000000..1fe5aee3 --- /dev/null +++ b/jslib/common/src/factories/accountFactory.ts @@ -0,0 +1,13 @@ +import { Account } from "../models/domain/account"; + +export class AccountFactory { + private accountConstructor: new (init: Partial) => T; + + constructor(accountConstructor: new (init: Partial) => T) { + this.accountConstructor = accountConstructor; + } + + create(args: Partial) { + return new this.accountConstructor(args); + } +} diff --git a/jslib/common/src/factories/globalStateFactory.ts b/jslib/common/src/factories/globalStateFactory.ts new file mode 100644 index 00000000..a2c25c46 --- /dev/null +++ b/jslib/common/src/factories/globalStateFactory.ts @@ -0,0 +1,13 @@ +import { GlobalState } from "../models/domain/globalState"; + +export class GlobalStateFactory { + private globalStateConstructor: new (init: Partial) => T; + + constructor(globalStateConstructor: new (init: Partial) => T) { + this.globalStateConstructor = globalStateConstructor; + } + + create(args?: Partial) { + return new this.globalStateConstructor(args); + } +} diff --git a/jslib/common/src/factories/stateFactory.ts b/jslib/common/src/factories/stateFactory.ts new file mode 100644 index 00000000..203ae672 --- /dev/null +++ b/jslib/common/src/factories/stateFactory.ts @@ -0,0 +1,29 @@ +import { Account } from "../models/domain/account"; +import { GlobalState } from "../models/domain/globalState"; + +import { AccountFactory } from "./accountFactory"; +import { GlobalStateFactory } from "./globalStateFactory"; + +export class StateFactory< + TGlobal extends GlobalState = GlobalState, + TAccount extends Account = Account +> { + private globalStateFactory: GlobalStateFactory; + private accountFactory: AccountFactory; + + constructor( + globalStateConstructor: new (init: Partial) => TGlobal, + accountConstructor: new (init: Partial) => TAccount + ) { + this.globalStateFactory = new GlobalStateFactory(globalStateConstructor); + this.accountFactory = new AccountFactory(accountConstructor); + } + + createGlobal(args: Partial): TGlobal { + return this.globalStateFactory.create(args); + } + + createAccount(args: Partial): TAccount { + return this.accountFactory.create(args); + } +} diff --git a/jslib/common/src/globals.d.ts b/jslib/common/src/globals.d.ts new file mode 100644 index 00000000..4859a086 --- /dev/null +++ b/jslib/common/src/globals.d.ts @@ -0,0 +1,2 @@ +declare function escape(s: string): string; +declare function unescape(s: string): string; diff --git a/jslib/common/src/importers/ascendoCsvImporter.ts b/jslib/common/src/importers/ascendoCsvImporter.ts new file mode 100644 index 00000000..75191a1c --- /dev/null +++ b/jslib/common/src/importers/ascendoCsvImporter.ts @@ -0,0 +1,59 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class AscendoCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 2) { + return; + } + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value[value.length - 1]); + cipher.name = this.getValueOrDefault(value[0], "--"); + + if (value.length > 2 && value.length % 2 === 0) { + for (let i = 0; i < value.length - 2; i += 2) { + const val: string = value[i + 2]; + const field: string = value[i + 1]; + if (this.isNullOrWhitespace(val) || this.isNullOrWhitespace(field)) { + continue; + } + + const fieldLower = field.toLowerCase(); + if (cipher.login.password == null && this.passwordFieldNames.indexOf(fieldLower) > -1) { + cipher.login.password = this.getValueOrDefault(val); + } else if ( + cipher.login.username == null && + this.usernameFieldNames.indexOf(fieldLower) > -1 + ) { + cipher.login.username = this.getValueOrDefault(val); + } else if ( + (cipher.login.uris == null || cipher.login.uris.length === 0) && + this.uriFieldNames.indexOf(fieldLower) > -1 + ) { + cipher.login.uris = this.makeUriArray(val); + } else { + this.processKvp(cipher, field, val); + } + } + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/avastCsvImporter.ts b/jslib/common/src/importers/avastCsvImporter.ts new file mode 100644 index 00000000..9bf10639 --- /dev/null +++ b/jslib/common/src/importers/avastCsvImporter.ts @@ -0,0 +1,28 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class AvastCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.name); + cipher.login.uris = this.makeUriArray(value.web); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.username = this.getValueOrDefault(value.login); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/avastJsonImporter.ts b/jslib/common/src/importers/avastJsonImporter.ts new file mode 100644 index 00000000..0d424a71 --- /dev/null +++ b/jslib/common/src/importers/avastJsonImporter.ts @@ -0,0 +1,68 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class AvastJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + if (results.logins != null) { + results.logins.forEach((value: any) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.custName); + cipher.notes = this.getValueOrDefault(value.note); + cipher.login.uris = this.makeUriArray(value.url); + cipher.login.password = this.getValueOrDefault(value.pwd); + cipher.login.username = this.getValueOrDefault(value.loginName); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + if (results.notes != null) { + results.notes.forEach((value: any) => { + const cipher = this.initLoginCipher(); + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + cipher.name = this.getValueOrDefault(value.label); + cipher.notes = this.getValueOrDefault(value.text); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + if (results.cards != null) { + results.cards.forEach((value: any) => { + const cipher = this.initLoginCipher(); + cipher.type = CipherType.Card; + cipher.name = this.getValueOrDefault(value.custName); + cipher.notes = this.getValueOrDefault(value.note); + cipher.card.cardholderName = this.getValueOrDefault(value.holderName); + cipher.card.number = this.getValueOrDefault(value.cardNumber); + cipher.card.code = this.getValueOrDefault(value.cvv); + cipher.card.brand = this.getCardBrand(cipher.card.number); + if (value.expirationDate != null) { + if (value.expirationDate.month != null) { + cipher.card.expMonth = value.expirationDate.month + ""; + } + if (value.expirationDate.year != null) { + cipher.card.expYear = value.expirationDate.year + ""; + } + } + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/aviraCsvImporter.ts b/jslib/common/src/importers/aviraCsvImporter.ts new file mode 100644 index 00000000..f4c578b0 --- /dev/null +++ b/jslib/common/src/importers/aviraCsvImporter.ts @@ -0,0 +1,41 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class AviraCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault( + value.name, + this.getValueOrDefault(this.nameFromUrl(value.website), "--") + ); + cipher.login.uris = this.makeUriArray(value.website); + cipher.login.password = this.getValueOrDefault(value.password); + + if ( + this.isNullOrWhitespace(value.username) && + !this.isNullOrWhitespace(value.secondary_username) + ) { + cipher.login.username = value.secondary_username; + } else { + cipher.login.username = this.getValueOrDefault(value.username); + cipher.notes = this.getValueOrDefault(value.secondary_username); + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/baseImporter.ts b/jslib/common/src/importers/baseImporter.ts new file mode 100644 index 00000000..122b3472 --- /dev/null +++ b/jslib/common/src/importers/baseImporter.ts @@ -0,0 +1,466 @@ +import * as papa from "papaparse"; + +import { LogService } from "../abstractions/log.service"; +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { Utils } from "../misc/utils"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { FieldView } from "../models/view/fieldView"; +import { FolderView } from "../models/view/folderView"; +import { LoginUriView } from "../models/view/loginUriView"; +import { LoginView } from "../models/view/loginView"; +import { SecureNoteView } from "../models/view/secureNoteView"; +import { ConsoleLogService } from "../services/consoleLog.service"; + +export abstract class BaseImporter { + organizationId: string = null; + + protected logService: LogService = new ConsoleLogService(false); + + protected newLineRegex = /(?:\r\n|\r|\n)/; + + protected passwordFieldNames = [ + "password", + "pass word", + "passphrase", + "pass phrase", + "pass", + "code", + "code word", + "codeword", + "secret", + "secret word", + "personpwd", + "key", + "keyword", + "key word", + "keyphrase", + "key phrase", + "form_pw", + "wppassword", + "pin", + "pwd", + "pw", + "pword", + "passwd", + "p", + "serial", + "serial#", + "license key", + "reg #", + + // Non-English names + "passwort", + ]; + + protected usernameFieldNames = [ + "user", + "name", + "user name", + "username", + "login name", + "email", + "e-mail", + "id", + "userid", + "user id", + "login", + "form_loginname", + "wpname", + "mail", + "loginid", + "login id", + "log", + "personlogin", + "first name", + "last name", + "card#", + "account #", + "member", + "member #", + + // Non-English names + "nom", + "benutzername", + ]; + + protected notesFieldNames = [ + "note", + "notes", + "comment", + "comments", + "memo", + "description", + "free form", + "freeform", + "free text", + "freetext", + "free", + + // Non-English names + "kommentar", + ]; + + protected uriFieldNames: string[] = [ + "url", + "hyper link", + "hyperlink", + "link", + "host", + "hostname", + "host name", + "server", + "address", + "hyper ref", + "href", + "web", + "website", + "web site", + "site", + "web-site", + "uri", + + // Non-English names + "ort", + "adresse", + ]; + + protected parseCsvOptions = { + encoding: "UTF-8", + skipEmptyLines: false, + }; + + protected get organization() { + return this.organizationId != null; + } + + protected parseXml(data: string): Document { + const parser = new DOMParser(); + const doc = parser.parseFromString(data, "application/xml"); + return doc != null && doc.querySelector("parsererror") == null ? doc : null; + } + + protected parseCsv(data: string, header: boolean, options: any = {}): any[] { + const parseOptions: papa.ParseConfig = Object.assign( + { header: header }, + this.parseCsvOptions, + options + ); + data = this.splitNewLine(data).join("\n").trim(); + const result = papa.parse(data, parseOptions); + if (result.errors != null && result.errors.length > 0) { + result.errors.forEach((e) => { + if (e.row != null) { + this.logService.warning("Error parsing row " + e.row + ": " + e.message); + } + }); + } + return result.data && result.data.length > 0 ? result.data : null; + } + + protected parseSingleRowCsv(rowData: string) { + if (this.isNullOrWhitespace(rowData)) { + return null; + } + const parsedRow = this.parseCsv(rowData, false); + if (parsedRow != null && parsedRow.length > 0 && parsedRow[0].length > 0) { + return parsedRow[0]; + } + return null; + } + + protected makeUriArray(uri: string | string[]): LoginUriView[] { + if (uri == null) { + return null; + } + + if (typeof uri === "string") { + const loginUri = new LoginUriView(); + loginUri.uri = this.fixUri(uri); + if (this.isNullOrWhitespace(loginUri.uri)) { + return null; + } + loginUri.match = null; + return [loginUri]; + } + + if (uri.length > 0) { + const returnArr: LoginUriView[] = []; + uri.forEach((u) => { + const loginUri = new LoginUriView(); + loginUri.uri = this.fixUri(u); + if (this.isNullOrWhitespace(loginUri.uri)) { + return; + } + loginUri.match = null; + returnArr.push(loginUri); + }); + return returnArr.length === 0 ? null : returnArr; + } + + return null; + } + + protected fixUri(uri: string) { + if (uri == null) { + return null; + } + uri = uri.trim(); + if (uri.indexOf("://") === -1 && uri.indexOf(".") >= 0) { + uri = "http://" + uri; + } + if (uri.length > 1000) { + return uri.substring(0, 1000); + } + return uri; + } + + protected nameFromUrl(url: string) { + const hostname = Utils.getHostname(url); + if (this.isNullOrWhitespace(hostname)) { + return null; + } + return hostname.startsWith("www.") ? hostname.replace("www.", "") : hostname; + } + + protected isNullOrWhitespace(str: string): boolean { + return Utils.isNullOrWhitespace(str); + } + + protected getValueOrDefault(str: string, defaultValue: string = null): string { + if (this.isNullOrWhitespace(str)) { + return defaultValue; + } + return str; + } + + protected splitNewLine(str: string): string[] { + return str.split(this.newLineRegex); + } + + // ref https://stackoverflow.com/a/5911300 + protected getCardBrand(cardNum: string) { + if (this.isNullOrWhitespace(cardNum)) { + return null; + } + + // Visa + let re = new RegExp("^4"); + if (cardNum.match(re) != null) { + return "Visa"; + } + + // Mastercard + // Updated for Mastercard 2017 BINs expansion + if ( + /^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/.test( + cardNum + ) + ) { + return "Mastercard"; + } + + // AMEX + re = new RegExp("^3[47]"); + if (cardNum.match(re) != null) { + return "Amex"; + } + + // Discover + re = new RegExp( + "^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)" + ); + if (cardNum.match(re) != null) { + return "Discover"; + } + + // Diners + re = new RegExp("^36"); + if (cardNum.match(re) != null) { + return "Diners Club"; + } + + // Diners - Carte Blanche + re = new RegExp("^30[0-5]"); + if (cardNum.match(re) != null) { + return "Diners Club"; + } + + // JCB + re = new RegExp("^35(2[89]|[3-8][0-9])"); + if (cardNum.match(re) != null) { + return "JCB"; + } + + // Visa Electron + re = new RegExp("^(4026|417500|4508|4844|491(3|7))"); + if (cardNum.match(re) != null) { + return "Visa"; + } + + return null; + } + + protected setCardExpiration(cipher: CipherView, expiration: string): boolean { + if (!this.isNullOrWhitespace(expiration)) { + expiration = expiration.replace(/\s/g, ""); + const parts = expiration.split("/"); + if (parts.length === 2) { + let month: string = null; + let year: string = null; + if (parts[0].length === 1 || parts[0].length === 2) { + month = parts[0]; + if (month.length === 2 && month[0] === "0") { + month = month.substr(1, 1); + } + } + if (parts[1].length === 2 || parts[1].length === 4) { + year = month.length === 2 ? "20" + parts[1] : parts[1]; + } + if (month != null && year != null) { + cipher.card.expMonth = month; + cipher.card.expYear = year; + return true; + } + } + } + return false; + } + + protected moveFoldersToCollections(result: ImportResult) { + result.folderRelationships.forEach((r) => result.collectionRelationships.push(r)); + result.collections = result.folders.map((f) => { + const collection = new CollectionView(); + collection.name = f.name; + return collection; + }); + result.folderRelationships = []; + result.folders = []; + } + + protected querySelectorDirectChild(parentEl: Element, query: string) { + const els = this.querySelectorAllDirectChild(parentEl, query); + return els.length === 0 ? null : els[0]; + } + + protected querySelectorAllDirectChild(parentEl: Element, query: string) { + return Array.from(parentEl.querySelectorAll(query)).filter((el) => el.parentNode === parentEl); + } + + protected initLoginCipher() { + const cipher = new CipherView(); + cipher.favorite = false; + cipher.notes = ""; + cipher.fields = []; + cipher.login = new LoginView(); + cipher.type = CipherType.Login; + return cipher; + } + + protected cleanupCipher(cipher: CipherView) { + if (cipher == null) { + return; + } + if (cipher.type !== CipherType.Login) { + cipher.login = null; + } + if (this.isNullOrWhitespace(cipher.name)) { + cipher.name = "--"; + } + if (this.isNullOrWhitespace(cipher.notes)) { + cipher.notes = null; + } else { + cipher.notes = cipher.notes.trim(); + } + if (cipher.fields != null && cipher.fields.length === 0) { + cipher.fields = null; + } + } + + protected processKvp( + cipher: CipherView, + key: string, + value: string, + type: FieldType = FieldType.Text + ) { + if (this.isNullOrWhitespace(value)) { + return; + } + if (this.isNullOrWhitespace(key)) { + key = ""; + } + if (value.length > 200 || value.trim().search(this.newLineRegex) > -1) { + if (cipher.notes == null) { + cipher.notes = ""; + } + cipher.notes += key + ": " + this.splitNewLine(value).join("\n") + "\n"; + } else { + if (cipher.fields == null) { + cipher.fields = []; + } + const field = new FieldView(); + field.type = type; + field.name = key; + field.value = value; + cipher.fields.push(field); + } + } + + protected processFolder(result: ImportResult, folderName: string) { + let folderIndex = result.folders.length; + const hasFolder = !this.isNullOrWhitespace(folderName); + let addFolder = hasFolder; + + if (hasFolder) { + for (let i = 0; i < result.folders.length; i++) { + if (result.folders[i].name === folderName) { + addFolder = false; + folderIndex = i; + break; + } + } + } + + if (addFolder) { + const f = new FolderView(); + f.name = folderName; + result.folders.push(f); + } + if (hasFolder) { + result.folderRelationships.push([result.ciphers.length, folderIndex]); + } + } + + protected convertToNoteIfNeeded(cipher: CipherView) { + if ( + cipher.type === CipherType.Login && + this.isNullOrWhitespace(cipher.login.username) && + this.isNullOrWhitespace(cipher.login.password) && + (cipher.login.uris == null || cipher.login.uris.length === 0) + ) { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + } + } + + protected processFullName(cipher: CipherView, fullName: string) { + if (this.isNullOrWhitespace(fullName)) { + return; + } + + const nameParts = fullName.split(" "); + if (nameParts.length > 0) { + cipher.identity.firstName = this.getValueOrDefault(nameParts[0]); + } + if (nameParts.length === 2) { + cipher.identity.lastName = this.getValueOrDefault(nameParts[1]); + } else if (nameParts.length >= 3) { + cipher.identity.middleName = this.getValueOrDefault(nameParts[1]); + cipher.identity.lastName = nameParts.slice(2, nameParts.length).join(" "); + } + } +} diff --git a/jslib/common/src/importers/bitwardenCsvImporter.ts b/jslib/common/src/importers/bitwardenCsvImporter.ts new file mode 100644 index 00000000..cc5419c3 --- /dev/null +++ b/jslib/common/src/importers/bitwardenCsvImporter.ts @@ -0,0 +1,120 @@ +import { CipherRepromptType } from "../enums/cipherRepromptType"; +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { FieldView } from "../models/view/fieldView"; +import { LoginView } from "../models/view/loginView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class BitwardenCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (this.organization && !this.isNullOrWhitespace(value.collections)) { + const collections = (value.collections as string).split(","); + collections.forEach((col) => { + let addCollection = true; + let collectionIndex = result.collections.length; + + for (let i = 0; i < result.collections.length; i++) { + if (result.collections[i].name === col) { + addCollection = false; + collectionIndex = i; + break; + } + } + + if (addCollection) { + const collection = new CollectionView(); + collection.name = col; + result.collections.push(collection); + } + + result.collectionRelationships.push([result.ciphers.length, collectionIndex]); + }); + } else if (!this.organization) { + this.processFolder(result, value.folder); + } + + const cipher = new CipherView(); + cipher.favorite = + !this.organization && this.getValueOrDefault(value.favorite, "0") !== "0" ? true : false; + cipher.type = CipherType.Login; + cipher.notes = this.getValueOrDefault(value.notes); + cipher.name = this.getValueOrDefault(value.name, "--"); + try { + cipher.reprompt = parseInt( + this.getValueOrDefault(value.reprompt, CipherRepromptType.None.toString()), + 10 + ); + } catch (e) { + // eslint-disable-next-line + console.error("Unable to parse reprompt value", e); + cipher.reprompt = CipherRepromptType.None; + } + + if (!this.isNullOrWhitespace(value.fields)) { + const fields = this.splitNewLine(value.fields); + for (let i = 0; i < fields.length; i++) { + if (this.isNullOrWhitespace(fields[i])) { + continue; + } + + const delimPosition = fields[i].lastIndexOf(": "); + if (delimPosition === -1) { + continue; + } + + if (cipher.fields == null) { + cipher.fields = []; + } + + const field = new FieldView(); + field.name = fields[i].substr(0, delimPosition); + field.value = null; + field.type = FieldType.Text; + if (fields[i].length > delimPosition + 2) { + field.value = fields[i].substr(delimPosition + 2); + } + cipher.fields.push(field); + } + } + + const valueType = value.type != null ? value.type.toLowerCase() : null; + switch (valueType) { + case "note": + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + break; + default: { + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.totp = this.getValueOrDefault(value.login_totp || value.totp); + cipher.login.username = this.getValueOrDefault(value.login_username || value.username); + cipher.login.password = this.getValueOrDefault(value.login_password || value.password); + const uris = this.parseSingleRowCsv(value.login_uri || value.uri); + cipher.login.uris = this.makeUriArray(uris); + break; + } + } + + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/bitwardenJsonImporter.ts b/jslib/common/src/importers/bitwardenJsonImporter.ts new file mode 100644 index 00000000..6caf185e --- /dev/null +++ b/jslib/common/src/importers/bitwardenJsonImporter.ts @@ -0,0 +1,179 @@ +import { CryptoService } from "../abstractions/crypto.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { EncString } from "../models/domain/encString"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherWithIds } from "../models/export/cipherWithIds"; +import { CollectionWithId } from "../models/export/collectionWithId"; +import { FolderWithId } from "../models/export/folderWithId"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class BitwardenJsonImporter extends BaseImporter implements Importer { + private results: any; + private result: ImportResult; + + constructor(protected cryptoService: CryptoService, protected i18nService: I18nService) { + super(); + } + + async parse(data: string): Promise { + this.result = new ImportResult(); + this.results = JSON.parse(data); + if (this.results == null || this.results.items == null) { + if (this.results?.passwordProtected) { + this.result.success = false; + this.result.missingPassword = true; + this.result.errorMessage = this.i18nService.t("importPasswordRequired"); + return this.result; + } + + this.result.success = false; + return this.result; + } + + if (this.results.encrypted) { + await this.parseEncrypted(); + } else { + this.parseDecrypted(); + } + + return this.result; + } + + private async parseEncrypted() { + if (this.results.encKeyValidation_DO_NOT_EDIT != null) { + const orgKey = await this.cryptoService.getOrgKey(this.organizationId); + const encKeyValidation = new EncString(this.results.encKeyValidation_DO_NOT_EDIT); + const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8( + encKeyValidation, + orgKey + ); + if (encKeyValidationDecrypt === null) { + this.result.success = false; + this.result.errorMessage = this.i18nService.t("importEncKeyError"); + return; + } + } + + const groupingsMap = new Map(); + + if (this.organization && this.results.collections != null) { + for (const c of this.results.collections as CollectionWithId[]) { + const collection = CollectionWithId.toDomain(c); + if (collection != null) { + collection.id = null; + collection.organizationId = this.organizationId; + const view = await collection.decrypt(); + groupingsMap.set(c.id, this.result.collections.length); + this.result.collections.push(view); + } + } + } else if (!this.organization && this.results.folders != null) { + for (const f of this.results.folders as FolderWithId[]) { + const folder = FolderWithId.toDomain(f); + if (folder != null) { + folder.id = null; + const view = await folder.decrypt(); + groupingsMap.set(f.id, this.result.folders.length); + this.result.folders.push(view); + } + } + } + + for (const c of this.results.items as CipherWithIds[]) { + const cipher = CipherWithIds.toDomain(c); + // reset ids incase they were set for some reason + cipher.id = null; + cipher.folderId = null; + cipher.organizationId = this.organizationId; + cipher.collectionIds = null; + + // make sure password history is limited + if (cipher.passwordHistory != null && cipher.passwordHistory.length > 5) { + cipher.passwordHistory = cipher.passwordHistory.slice(0, 5); + } + + if (!this.organization && c.folderId != null && groupingsMap.has(c.folderId)) { + this.result.folderRelationships.push([ + this.result.ciphers.length, + groupingsMap.get(c.folderId), + ]); + } else if (this.organization && c.collectionIds != null) { + c.collectionIds.forEach((cId) => { + if (groupingsMap.has(cId)) { + this.result.collectionRelationships.push([ + this.result.ciphers.length, + groupingsMap.get(cId), + ]); + } + }); + } + + const view = await cipher.decrypt(); + this.cleanupCipher(view); + this.result.ciphers.push(view); + } + + this.result.success = true; + } + + private parseDecrypted() { + const groupingsMap = new Map(); + if (this.organization && this.results.collections != null) { + this.results.collections.forEach((c: CollectionWithId) => { + const collection = CollectionWithId.toView(c); + if (collection != null) { + collection.id = null; + collection.organizationId = null; + groupingsMap.set(c.id, this.result.collections.length); + this.result.collections.push(collection); + } + }); + } else if (!this.organization && this.results.folders != null) { + this.results.folders.forEach((f: FolderWithId) => { + const folder = FolderWithId.toView(f); + if (folder != null) { + folder.id = null; + groupingsMap.set(f.id, this.result.folders.length); + this.result.folders.push(folder); + } + }); + } + + this.results.items.forEach((c: CipherWithIds) => { + const cipher = CipherWithIds.toView(c); + // reset ids incase they were set for some reason + cipher.id = null; + cipher.folderId = null; + cipher.organizationId = null; + cipher.collectionIds = null; + + // make sure password history is limited + if (cipher.passwordHistory != null && cipher.passwordHistory.length > 5) { + cipher.passwordHistory = cipher.passwordHistory.slice(0, 5); + } + + if (!this.organization && c.folderId != null && groupingsMap.has(c.folderId)) { + this.result.folderRelationships.push([ + this.result.ciphers.length, + groupingsMap.get(c.folderId), + ]); + } else if (this.organization && c.collectionIds != null) { + c.collectionIds.forEach((cId) => { + if (groupingsMap.has(cId)) { + this.result.collectionRelationships.push([ + this.result.ciphers.length, + groupingsMap.get(cId), + ]); + } + }); + } + + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + + this.result.success = true; + } +} diff --git a/jslib/common/src/importers/bitwardenPasswordProtectedImporter.ts b/jslib/common/src/importers/bitwardenPasswordProtectedImporter.ts new file mode 100644 index 00000000..54da71b5 --- /dev/null +++ b/jslib/common/src/importers/bitwardenPasswordProtectedImporter.ts @@ -0,0 +1,81 @@ +import { CryptoService } from "../abstractions/crypto.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { KdfType } from "../enums/kdfType"; +import { EncString } from "../models/domain/encString"; +import { ImportResult } from "../models/domain/importResult"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; + +import { BitwardenJsonImporter } from "./bitwardenJsonImporter"; +import { Importer } from "./importer"; + +interface BitwardenPasswordProtectedFileFormat { + encrypted: boolean; + passwordProtected: boolean; + salt: string; + kdfIterations: number; + kdfType: number; + encKeyValidation_DO_NOT_EDIT: string; + data: string; +} + +export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter implements Importer { + private key: SymmetricCryptoKey; + + constructor(cryptoService: CryptoService, i18nService: I18nService, private password: string) { + super(cryptoService, i18nService); + } + + async parse(data: string): Promise { + const result = new ImportResult(); + const parsedData = JSON.parse(data); + if (this.cannotParseFile(parsedData)) { + result.success = false; + return result; + } + + if (!(await this.checkPassword(parsedData))) { + result.success = false; + result.errorMessage = this.i18nService.t("importEncKeyError"); + return result; + } + + const encData = new EncString(parsedData.data); + const clearTextData = await this.cryptoService.decryptToUtf8(encData, this.key); + return await super.parse(clearTextData); + } + + private async checkPassword(jdoc: BitwardenPasswordProtectedFileFormat): Promise { + this.key = await this.cryptoService.makePinKey( + this.password, + jdoc.salt, + KdfType.PBKDF2_SHA256, + jdoc.kdfIterations + ); + + const encKeyValidation = new EncString(jdoc.encKeyValidation_DO_NOT_EDIT); + + const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8( + encKeyValidation, + this.key + ); + if (encKeyValidationDecrypt === null) { + return false; + } + return true; + } + + private cannotParseFile(jdoc: BitwardenPasswordProtectedFileFormat): boolean { + return ( + !jdoc || + !jdoc.encrypted || + !jdoc.passwordProtected || + !jdoc.salt || + !jdoc.kdfIterations || + typeof jdoc.kdfIterations !== "number" || + jdoc.kdfType == null || + KdfType[jdoc.kdfType] == null || + !jdoc.encKeyValidation_DO_NOT_EDIT || + !jdoc.data + ); + } +} diff --git a/jslib/common/src/importers/blackBerryCsvImporter.ts b/jslib/common/src/importers/blackBerryCsvImporter.ts new file mode 100644 index 00000000..ea3d08eb --- /dev/null +++ b/jslib/common/src/importers/blackBerryCsvImporter.ts @@ -0,0 +1,36 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class BlackBerryCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.grouping === "list") { + return; + } + const cipher = this.initLoginCipher(); + cipher.favorite = value.fav === "1"; + cipher.name = this.getValueOrDefault(value.name); + cipher.notes = this.getValueOrDefault(value.extra); + if (value.grouping !== "note") { + cipher.login.uris = this.makeUriArray(value.url); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.username = this.getValueOrDefault(value.username); + } + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/blurCsvImporter.ts b/jslib/common/src/importers/blurCsvImporter.ts new file mode 100644 index 00000000..ca9ecd1c --- /dev/null +++ b/jslib/common/src/importers/blurCsvImporter.ts @@ -0,0 +1,41 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class BlurCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.label === "null") { + value.label = null; + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault( + value.label, + this.getValueOrDefault(this.nameFromUrl(value.domain), "--") + ); + cipher.login.uris = this.makeUriArray(value.domain); + cipher.login.password = this.getValueOrDefault(value.password); + + if (this.isNullOrWhitespace(value.email) && !this.isNullOrWhitespace(value.username)) { + cipher.login.username = value.username; + } else { + cipher.login.username = this.getValueOrDefault(value.email); + cipher.notes = this.getValueOrDefault(value.username); + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/buttercupCsvImporter.ts b/jslib/common/src/importers/buttercupCsvImporter.ts new file mode 100644 index 00000000..aa3dda2d --- /dev/null +++ b/jslib/common/src/importers/buttercupCsvImporter.ts @@ -0,0 +1,50 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +const OfficialProps = ["!group_id", "!group_name", "title", "username", "password", "URL", "id"]; + +export class ButtercupCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + this.processFolder(result, this.getValueOrDefault(value["!group_name"])); + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.title, "--"); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.URL); + + let processingCustomFields = false; + for (const prop in value) { + // eslint-disable-next-line + if (value.hasOwnProperty(prop)) { + if (!processingCustomFields && OfficialProps.indexOf(prop) === -1) { + processingCustomFields = true; + } + if (processingCustomFields) { + this.processKvp(cipher, prop, value[prop]); + } + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/chromeCsvImporter.ts b/jslib/common/src/importers/chromeCsvImporter.ts new file mode 100644 index 00000000..35c72ac3 --- /dev/null +++ b/jslib/common/src/importers/chromeCsvImporter.ts @@ -0,0 +1,28 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class ChromeCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.name, "--"); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.url); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/clipperzHtmlImporter.ts b/jslib/common/src/importers/clipperzHtmlImporter.ts new file mode 100644 index 00000000..c4f0402c --- /dev/null +++ b/jslib/common/src/importers/clipperzHtmlImporter.ts @@ -0,0 +1,88 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class ClipperzHtmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const textarea = doc.querySelector("textarea"); + if (textarea == null || this.isNullOrWhitespace(textarea.textContent)) { + result.errorMessage = "Missing textarea."; + result.success = false; + return Promise.resolve(result); + } + + const entries = JSON.parse(textarea.textContent); + entries.forEach((entry: any) => { + const cipher = this.initLoginCipher(); + if (!this.isNullOrWhitespace(entry.label)) { + cipher.name = entry.label.split(" ")[0]; + } + if (entry.data != null && !this.isNullOrWhitespace(entry.data.notes)) { + cipher.notes = entry.data.notes.split("\\n").join("\n"); + } + + if (entry.currentVersion != null && entry.currentVersion.fields != null) { + for (const property in entry.currentVersion.fields) { + // eslint-disable-next-line + if (!entry.currentVersion.fields.hasOwnProperty(property)) { + continue; + } + + const field = entry.currentVersion.fields[property]; + const actionType = field.actionType != null ? field.actionType.toLowerCase() : null; + switch (actionType) { + case "password": + cipher.login.password = this.getValueOrDefault(field.value); + break; + case "email": + case "username": + case "user": + case "name": + cipher.login.username = this.getValueOrDefault(field.value); + break; + case "url": + cipher.login.uris = this.makeUriArray(field.value); + break; + default: { + const labelLower = field.label != null ? field.label.toLowerCase() : null; + if ( + cipher.login.password == null && + this.passwordFieldNames.indexOf(labelLower) > -1 + ) { + cipher.login.password = this.getValueOrDefault(field.value); + } else if ( + cipher.login.username == null && + this.usernameFieldNames.indexOf(labelLower) > -1 + ) { + cipher.login.username = this.getValueOrDefault(field.value); + } else if ( + (cipher.login.uris == null || cipher.login.uris.length === 0) && + this.uriFieldNames.indexOf(labelLower) > -1 + ) { + cipher.login.uris = this.makeUriArray(field.value); + } else { + this.processKvp(cipher, field.label, field.value); + } + break; + } + } + } + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/codebookCsvImporter.ts b/jslib/common/src/importers/codebookCsvImporter.ts new file mode 100644 index 00000000..f842f21c --- /dev/null +++ b/jslib/common/src/importers/codebookCsvImporter.ts @@ -0,0 +1,47 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class CodebookCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + this.processFolder(result, this.getValueOrDefault(value.Category)); + + const cipher = this.initLoginCipher(); + cipher.favorite = this.getValueOrDefault(value.Favorite) === "True"; + cipher.name = this.getValueOrDefault(value.Entry, "--"); + cipher.notes = this.getValueOrDefault(value.Note); + cipher.login.username = this.getValueOrDefault(value.Username, value.Email); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.totp = this.getValueOrDefault(value.TOTP); + cipher.login.uris = this.makeUriArray(value.Website); + + if (!this.isNullOrWhitespace(value.Username)) { + this.processKvp(cipher, "Email", value.Email); + } + this.processKvp(cipher, "Phone", value.Phone); + this.processKvp(cipher, "PIN", value.PIN); + this.processKvp(cipher, "Account", value.Account); + this.processKvp(cipher, "Date", value.Date); + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/dashlaneImporters/dashlaneCsvImporter.ts b/jslib/common/src/importers/dashlaneImporters/dashlaneCsvImporter.ts new file mode 100644 index 00000000..2bb6ce14 --- /dev/null +++ b/jslib/common/src/importers/dashlaneImporters/dashlaneCsvImporter.ts @@ -0,0 +1,271 @@ +import { CipherType } from "../../enums/cipherType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { LoginView } from "../../models/view/loginView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { + CredentialsRecord, + IdRecord, + PaymentsRecord, + PersonalInformationRecord, + SecureNoteRecord, +} from "./types/dashlaneCsvTypes"; + +const _mappedCredentialsColums = new Set([ + "title", + "note", + "username", + "password", + "url", + "otpSecret", + "category", +]); + +const _mappedPersonalInfoAsIdentiyColumns = new Set([ + "type", + "title", + "first_name", + "middle_name", + "last_name", + "login", + "email", + "phone_number", + "address", + "country", + "state", + "city", + "zip", + // Skip item_name as we already have set a combined name + "item_name", +]); + +const _mappedSecureNoteColumns = new Set(["title", "note"]); + +export class DashlaneCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + if (results[0].type != null && results[0].title != null) { + const personalRecords = results as PersonalInformationRecord[]; + + // If personalRecords has only one "name" then create an Identity-Cipher + if (personalRecords.filter((x) => x.type === "name").length === 1) { + const cipher = this.initLoginCipher(); + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + results.forEach((row) => { + this.parsePersonalInformationRecordAsIdentity(cipher, row); + }); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + result.success = true; + return Promise.resolve(result); + } + } + + results.forEach((row) => { + const cipher = this.initLoginCipher(); + + const rowKeys = Object.keys(row); + if (rowKeys[0] === "username") { + this.processFolder(result, row.category); + this.parseCredentialsRecord(cipher, row); + } + + if (rowKeys[0] === "type" && rowKeys[1] === "account_name") { + this.parsePaymentRecord(cipher, row); + } + + if (rowKeys[0] === "type" && rowKeys[1] === "number") { + this.parseIdRecord(cipher, row); + } + + if ((rowKeys[0] === "type") != null && rowKeys[1] === "title") { + this.parsePersonalInformationRecord(cipher, row); + } + + if (rowKeys[0] === "title" && rowKeys[1] === "note") { + this.parseSecureNoteRecords(cipher, row); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + parseCredentialsRecord(cipher: CipherView, row: CredentialsRecord) { + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + + cipher.name = row.title; + cipher.notes = row.note; + cipher.login.username = row.username; + cipher.login.password = row.password; + cipher.login.totp = row.otpSecret; + cipher.login.uris = this.makeUriArray(row.url); + + this.importUnmappedFields(cipher, row, _mappedCredentialsColums); + } + + parsePaymentRecord(cipher: CipherView, row: PaymentsRecord) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + + cipher.name = row.account_name; + let mappedValues: string[] = []; + switch (row.type) { + case "credit_card": + cipher.card.cardholderName = row.account_name; + cipher.card.number = row.cc_number; + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = row.code; + cipher.card.expMonth = row.expiration_month; + cipher.card.expYear = row.expiration_year.substring(2, 4); + + // If you add more mapped fields please extend this + mappedValues = [ + "account_name", + "account_holder", + "cc_number", + "code", + "expiration_month", + "expiration_year", + ]; + break; + case "bank": + cipher.card.cardholderName = row.account_holder; + cipher.card.number = row.account_number; + + // If you add more mapped fields please extend this + mappedValues = ["account_name", "account_holder", "account_number"]; + break; + default: + break; + } + + this.importUnmappedFields(cipher, row, new Set(mappedValues)); + } + + parseIdRecord(cipher: CipherView, row: IdRecord) { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + + const mappedValues: string[] = ["name", "number"]; + switch (row.type) { + case "card": + cipher.name = `${row.name} ${row.type}`; + this.processFullName(cipher, row.name); + cipher.identity.licenseNumber = row.number; + break; + case "passport": + cipher.name = `${row.name} ${row.type}`; + this.processFullName(cipher, row.name); + cipher.identity.passportNumber = row.number; + break; + case "license": + cipher.name = `${row.name} ${row.type}`; + this.processFullName(cipher, row.name); + cipher.identity.licenseNumber = row.number; + cipher.identity.state = row.state; + + mappedValues.push("state"); + break; + case "social_security": + cipher.name = `${row.name} ${row.type}`; + this.processFullName(cipher, row.name); + cipher.identity.ssn = row.number; + break; + case "tax_number": + cipher.name = row.type; + cipher.identity.licenseNumber = row.number; + break; + + default: + break; + } + + // If you add more mapped fields please extend this + this.importUnmappedFields(cipher, row, new Set(mappedValues)); + } + + parsePersonalInformationRecord(cipher: CipherView, row: PersonalInformationRecord) { + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + if (row.type === "name") { + cipher.name = `${row.title} ${row.first_name} ${row.middle_name} ${row.last_name}` + .replace(" ", " ") + .trim(); + } else { + cipher.name = row.item_name; + } + + const dataRow = row as any; + Object.keys(row).forEach((key) => { + this.processKvp(cipher, key, dataRow[key]); + }); + } + + parsePersonalInformationRecordAsIdentity(cipher: CipherView, row: PersonalInformationRecord) { + switch (row.type) { + case "name": + this.processFullName(cipher, `${row.first_name} ${row.middle_name} ${row.last_name}`); + cipher.identity.title = row.title; + cipher.name = cipher.identity.fullName; + + cipher.identity.username = row.login; + break; + case "email": + cipher.identity.email = row.email; + break; + case "number": + cipher.identity.phone = row.phone_number; + break; + case "address": + cipher.identity.address1 = row.address; + cipher.identity.city = row.city; + cipher.identity.postalCode = row.zip; + cipher.identity.state = row.state; + cipher.identity.country = row.country; + break; + default: + break; + } + + this.importUnmappedFields(cipher, row, _mappedPersonalInfoAsIdentiyColumns); + } + + parseSecureNoteRecords(cipher: CipherView, row: SecureNoteRecord) { + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + cipher.name = row.title; + cipher.notes = row.note; + + this.importUnmappedFields(cipher, row, _mappedSecureNoteColumns); + } + + importUnmappedFields(cipher: CipherView, row: any, mappedValues: Set) { + const unmappedFields = Object.keys(row).filter((x) => !mappedValues.has(x)); + unmappedFields.forEach((key) => { + const item = row as any; + this.processKvp(cipher, key, item[key]); + }); + } +} diff --git a/jslib/common/src/importers/dashlaneImporters/dashlaneJsonImporter.ts b/jslib/common/src/importers/dashlaneImporters/dashlaneJsonImporter.ts new file mode 100644 index 00000000..cb7db59d --- /dev/null +++ b/jslib/common/src/importers/dashlaneImporters/dashlaneJsonImporter.ts @@ -0,0 +1,171 @@ +import { CipherType } from "../../enums/cipherType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { SecureNoteView } from "../../models/view/secureNoteView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +const HandledResults = new Set([ + "ADDRESS", + "AUTHENTIFIANT", + "BANKSTATEMENT", + "IDCARD", + "IDENTITY", + "PAYMENTMEANS_CREDITCARD", + "PAYMENTMEAN_PAYPAL", + "EMAIL", +]); + +export class DashlaneJsonImporter extends BaseImporter implements Importer { + private result: ImportResult; + + parse(data: string): Promise { + this.result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.length === 0) { + this.result.success = false; + return Promise.resolve(this.result); + } + + if (results.ADDRESS != null) { + this.processAddress(results.ADDRESS); + } + if (results.AUTHENTIFIANT != null) { + this.processAuth(results.AUTHENTIFIANT); + } + if (results.BANKSTATEMENT != null) { + this.processNote(results.BANKSTATEMENT, "BankAccountName"); + } + if (results.IDCARD != null) { + this.processNote(results.IDCARD, "Fullname"); + } + if (results.PAYMENTMEANS_CREDITCARD != null) { + this.processCard(results.PAYMENTMEANS_CREDITCARD); + } + if (results.IDENTITY != null) { + this.processIdentity(results.IDENTITY); + } + + for (const key in results) { + // eslint-disable-next-line + if (results.hasOwnProperty(key) && !HandledResults.has(key)) { + this.processNote(results[key], null, "Generic Note"); + } + } + + this.result.success = true; + return Promise.resolve(this.result); + } + + private processAuth(results: any[]) { + results.forEach((credential: any) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(credential.title); + + cipher.login.username = this.getValueOrDefault( + credential.login, + this.getValueOrDefault(credential.secondaryLogin) + ); + if (this.isNullOrWhitespace(cipher.login.username)) { + cipher.login.username = this.getValueOrDefault(credential.email); + } else if (!this.isNullOrWhitespace(credential.email)) { + cipher.notes = "Email: " + credential.email + "\n"; + } + + cipher.login.password = this.getValueOrDefault(credential.password); + cipher.login.uris = this.makeUriArray(credential.domain); + cipher.notes += this.getValueOrDefault(credential.note, ""); + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } + + private processIdentity(results: any[]) { + results.forEach((obj: any) => { + const cipher = new CipherView(); + cipher.identity = new IdentityView(); + cipher.type = CipherType.Identity; + cipher.name = this.getValueOrDefault(obj.fullName, ""); + const nameParts = cipher.name.split(" "); + if (nameParts.length > 0) { + cipher.identity.firstName = this.getValueOrDefault(nameParts[0]); + } + if (nameParts.length === 2) { + cipher.identity.lastName = this.getValueOrDefault(nameParts[1]); + } else if (nameParts.length === 3) { + cipher.identity.middleName = this.getValueOrDefault(nameParts[1]); + cipher.identity.lastName = this.getValueOrDefault(nameParts[2]); + } + cipher.identity.username = this.getValueOrDefault(obj.pseudo); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } + + private processAddress(results: any[]) { + results.forEach((obj: any) => { + const cipher = new CipherView(); + cipher.identity = new IdentityView(); + cipher.type = CipherType.Identity; + cipher.name = this.getValueOrDefault(obj.addressName); + cipher.identity.address1 = this.getValueOrDefault(obj.addressFull); + cipher.identity.city = this.getValueOrDefault(obj.city); + cipher.identity.state = this.getValueOrDefault(obj.state); + cipher.identity.postalCode = this.getValueOrDefault(obj.zipcode); + cipher.identity.country = this.getValueOrDefault(obj.country); + if (cipher.identity.country != null) { + cipher.identity.country = cipher.identity.country.toUpperCase(); + } + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } + + private processCard(results: any[]) { + results.forEach((obj: any) => { + const cipher = new CipherView(); + cipher.card = new CardView(); + cipher.type = CipherType.Card; + cipher.name = this.getValueOrDefault(obj.bank); + cipher.card.number = this.getValueOrDefault(obj.cardNumber); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.cardholderName = this.getValueOrDefault(obj.owner); + if (!this.isNullOrWhitespace(cipher.card.brand)) { + if (this.isNullOrWhitespace(cipher.name)) { + cipher.name = cipher.card.brand; + } else { + cipher.name += " - " + cipher.card.brand; + } + } + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } + + private processNote(results: any[], nameProperty: string, name: string = null) { + results.forEach((obj: any) => { + const cipher = new CipherView(); + cipher.secureNote = new SecureNoteView(); + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + if (name != null) { + cipher.name = name; + } else { + cipher.name = this.getValueOrDefault(obj[nameProperty]); + } + for (const key in obj) { + // eslint-disable-next-line + if (obj.hasOwnProperty(key) && key !== nameProperty) { + this.processKvp(cipher, key, obj[key].toString()); + } + } + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } +} diff --git a/jslib/common/src/importers/dashlaneImporters/types/dashlaneCsvTypes.ts b/jslib/common/src/importers/dashlaneImporters/types/dashlaneCsvTypes.ts new file mode 100644 index 00000000..cb321c56 --- /dev/null +++ b/jslib/common/src/importers/dashlaneImporters/types/dashlaneCsvTypes.ts @@ -0,0 +1,68 @@ +// tslint:disable +export class CredentialsRecord { + username: string; + username2: string; + username3: string; + title: string; + password: string; + note: string; + url: string; + category: string; + otpSecret: string; +} + +export class PaymentsRecord { + type: string; + account_name: string; + account_holder: string; + cc_number: string; + code: string; + expiration_month: string; + expiration_year: string; + routing_number: string; + account_number: string; + country: string; + issuing_bank: string; +} + +export class IdRecord { + type: string; + number: string; + name: string; + issue_date: string; + expiration_date: string; + place_of_issue: string; + state: string; +} + +export class PersonalInformationRecord { + type: string; + title: string; + first_name: string; + middle_name: string; + last_name: string; + login: string; + date_of_birth: string; + place_of_birth: string; + email: string; + email_type: string; + item_name: string; + phone_number: string; + address: string; + country: string; + state: string; + city: string; + zip: string; + address_recipient: string; + address_building: string; + address_apartment: string; + address_floor: string; + address_door_code: string; + job_title: string; + url: string; +} + +export class SecureNoteRecord { + title: string; + note: string; +} diff --git a/jslib/common/src/importers/encryptrCsvImporter.ts b/jslib/common/src/importers/encryptrCsvImporter.ts new file mode 100644 index 00000000..8c3e9e08 --- /dev/null +++ b/jslib/common/src/importers/encryptrCsvImporter.ts @@ -0,0 +1,60 @@ +import { CipherType } from "../enums/cipherType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class EncryptrCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.Label, "--"); + cipher.notes = this.getValueOrDefault(value.Notes); + const text = this.getValueOrDefault(value.Text); + if (!this.isNullOrWhitespace(text)) { + if (this.isNullOrWhitespace(cipher.notes)) { + cipher.notes = text; + } else { + cipher.notes += "\n\n" + text; + } + } + + const type = value["Entry Type"]; + if (type === "Password") { + cipher.login.username = this.getValueOrDefault(value.Username); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value["Site URL"]); + } else if (type === "Credit Card") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(value["Name on card"]); + cipher.card.number = this.getValueOrDefault(value["Card Number"]); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = this.getValueOrDefault(value.CVV); + const expiry = this.getValueOrDefault(value.Expiry); + if (!this.isNullOrWhitespace(expiry)) { + const expParts = expiry.split("/"); + if (expParts.length > 1) { + cipher.card.expMonth = parseInt(expParts[0], null).toString(); + cipher.card.expYear = (2000 + parseInt(expParts[1], null)).toString(); + } + } + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/enpassCsvImporter.ts b/jslib/common/src/importers/enpassCsvImporter.ts new file mode 100644 index 00000000..b5eb0b60 --- /dev/null +++ b/jslib/common/src/importers/enpassCsvImporter.ts @@ -0,0 +1,133 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class EnpassCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + let firstRow = true; + results.forEach((value) => { + if (value.length < 2 || (firstRow && (value[0] === "Title" || value[0] === "title"))) { + firstRow = false; + return; + } + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value[value.length - 1]); + cipher.name = this.getValueOrDefault(value[0], "--"); + + if ( + value.length === 2 || + (!this.containsField(value, "username") && + !this.containsField(value, "password") && + !this.containsField(value, "email") && + !this.containsField(value, "url")) + ) { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + } + + if ( + this.containsField(value, "cardholder") && + this.containsField(value, "number") && + this.containsField(value, "expiry date") + ) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } + + if (value.length > 2 && value.length % 2 === 0) { + for (let i = 0; i < value.length - 2; i += 2) { + const fieldValue: string = value[i + 2]; + if (this.isNullOrWhitespace(fieldValue)) { + continue; + } + + const fieldName: string = value[i + 1]; + const fieldNameLower = fieldName.toLowerCase(); + + if (cipher.type === CipherType.Login) { + if ( + fieldNameLower === "url" && + (cipher.login.uris == null || cipher.login.uris.length === 0) + ) { + cipher.login.uris = this.makeUriArray(fieldValue); + continue; + } else if ( + (fieldNameLower === "username" || fieldNameLower === "email") && + this.isNullOrWhitespace(cipher.login.username) + ) { + cipher.login.username = fieldValue; + continue; + } else if ( + fieldNameLower === "password" && + this.isNullOrWhitespace(cipher.login.password) + ) { + cipher.login.password = fieldValue; + continue; + } else if (fieldNameLower === "totp" && this.isNullOrWhitespace(cipher.login.totp)) { + cipher.login.totp = fieldValue; + continue; + } + } else if (cipher.type === CipherType.Card) { + if ( + fieldNameLower === "cardholder" && + this.isNullOrWhitespace(cipher.card.cardholderName) + ) { + cipher.card.cardholderName = fieldValue; + continue; + } else if (fieldNameLower === "number" && this.isNullOrWhitespace(cipher.card.number)) { + cipher.card.number = fieldValue; + cipher.card.brand = this.getCardBrand(fieldValue); + continue; + } else if (fieldNameLower === "cvc" && this.isNullOrWhitespace(cipher.card.code)) { + cipher.card.code = fieldValue; + continue; + } else if ( + fieldNameLower === "expiry date" && + this.isNullOrWhitespace(cipher.card.expMonth) && + this.isNullOrWhitespace(cipher.card.expYear) + ) { + if (this.setCardExpiration(cipher, fieldValue)) { + continue; + } + } else if (fieldNameLower === "type") { + // Skip since brand was determined from number above + continue; + } + } + + this.processKvp(cipher, fieldName, fieldValue); + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + private containsField(fields: any[], name: string) { + if (fields == null || name == null) { + return false; + } + return ( + fields.filter((f) => !this.isNullOrWhitespace(f) && f.toLowerCase() === name.toLowerCase()) + .length > 0 + ); + } +} diff --git a/jslib/common/src/importers/enpassJsonImporter.ts b/jslib/common/src/importers/enpassJsonImporter.ts new file mode 100644 index 00000000..033c5995 --- /dev/null +++ b/jslib/common/src/importers/enpassJsonImporter.ts @@ -0,0 +1,191 @@ +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { CipherView } from "../models/view/cipherView"; +import { FolderView } from "../models/view/folderView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class EnpassJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.items == null || results.items.length === 0) { + result.success = false; + return Promise.resolve(result); + } + + const foldersMap = new Map(); + const foldersIndexMap = new Map(); + const folderTree = this.buildFolderTree(results.folders); + this.flattenFolderTree(null, folderTree, foldersMap); + foldersMap.forEach((val, key) => { + foldersIndexMap.set(key, result.folders.length); + const f = new FolderView(); + f.name = val; + result.folders.push(f); + }); + + results.items.forEach((item: any) => { + if (item.folders != null && item.folders.length > 0 && foldersIndexMap.has(item.folders[0])) { + result.folderRelationships.push([ + result.ciphers.length, + foldersIndexMap.get(item.folders[0]), + ]); + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(item.title); + cipher.favorite = item.favorite > 0; + + if (item.template_type != null && item.fields != null && item.fields.length > 0) { + if ( + item.template_type.indexOf("login.") === 0 || + item.template_type.indexOf("password.") === 0 + ) { + this.processLogin(cipher, item.fields); + } else if (item.template_type.indexOf("creditcard.") === 0) { + this.processCard(cipher, item.fields); + } else if ( + item.template_type.indexOf("identity.") < 0 && + item.fields.some((f: any) => f.type === "password" && !this.isNullOrWhitespace(f.value)) + ) { + this.processLogin(cipher, item.fields); + } else { + this.processNote(cipher, item.fields); + } + } + + cipher.notes += "\n" + this.getValueOrDefault(item.note, ""); + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + private processLogin(cipher: CipherView, fields: any[]) { + const urls: string[] = []; + fields.forEach((field: any) => { + if (this.isNullOrWhitespace(field.value) || field.type === "section") { + return; + } + + if ( + (field.type === "username" || field.type === "email") && + this.isNullOrWhitespace(cipher.login.username) + ) { + cipher.login.username = field.value; + } else if (field.type === "password" && this.isNullOrWhitespace(cipher.login.password)) { + cipher.login.password = field.value; + } else if (field.type === "totp" && this.isNullOrWhitespace(cipher.login.totp)) { + cipher.login.totp = field.value; + } else if (field.type === "url") { + urls.push(field.value); + } else { + this.processKvp( + cipher, + field.label, + field.value, + field.sensitive === 1 ? FieldType.Hidden : FieldType.Text + ); + } + }); + cipher.login.uris = this.makeUriArray(urls); + } + + private processCard(cipher: CipherView, fields: any[]) { + cipher.card = new CardView(); + cipher.type = CipherType.Card; + fields.forEach((field: any) => { + if ( + this.isNullOrWhitespace(field.value) || + field.type === "section" || + field.type === "ccType" + ) { + return; + } + + if (field.type === "ccName" && this.isNullOrWhitespace(cipher.card.cardholderName)) { + cipher.card.cardholderName = field.value; + } else if (field.type === "ccNumber" && this.isNullOrWhitespace(cipher.card.number)) { + cipher.card.number = field.value; + cipher.card.brand = this.getCardBrand(cipher.card.number); + } else if (field.type === "ccCvc" && this.isNullOrWhitespace(cipher.card.code)) { + cipher.card.code = field.value; + } else if (field.type === "ccExpiry" && this.isNullOrWhitespace(cipher.card.expYear)) { + if (!this.setCardExpiration(cipher, field.value)) { + this.processKvp( + cipher, + field.label, + field.value, + field.sensitive === 1 ? FieldType.Hidden : FieldType.Text + ); + } + } else { + this.processKvp( + cipher, + field.label, + field.value, + field.sensitive === 1 ? FieldType.Hidden : FieldType.Text + ); + } + }); + } + + private processNote(cipher: CipherView, fields: any[]) { + fields.forEach((field: any) => { + if (this.isNullOrWhitespace(field.value) || field.type === "section") { + return; + } + this.processKvp( + cipher, + field.label, + field.value, + field.sensitive === 1 ? FieldType.Hidden : FieldType.Text + ); + }); + } + + private buildFolderTree(folders: any[]): any[] { + if (folders == null) { + return []; + } + const folderTree: any[] = []; + const map = new Map([]); + folders.forEach((obj: any) => { + map.set(obj.uuid, obj); + obj.children = []; + }); + folders.forEach((obj: any) => { + if (obj.parent_uuid != null && obj.parent_uuid !== "" && map.has(obj.parent_uuid)) { + map.get(obj.parent_uuid).children.push(obj); + } else { + folderTree.push(obj); + } + }); + return folderTree; + } + + private flattenFolderTree(titlePrefix: string, tree: any[], map: Map) { + if (tree == null) { + return; + } + tree.forEach((f: any) => { + if (f.title != null && f.title.trim() !== "") { + let title = f.title.trim(); + if (titlePrefix != null && titlePrefix.trim() !== "") { + title = titlePrefix + "/" + title; + } + map.set(f.uuid, title); + if (f.children != null && f.children.length !== 0) { + this.flattenFolderTree(title, f.children, map); + } + } + }); + } +} diff --git a/jslib/common/src/importers/firefoxCsvImporter.ts b/jslib/common/src/importers/firefoxCsvImporter.ts new file mode 100644 index 00000000..1b71aac9 --- /dev/null +++ b/jslib/common/src/importers/firefoxCsvImporter.ts @@ -0,0 +1,33 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class FirefoxCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results + .filter((value) => { + return value.url !== "chrome://FirefoxAccounts"; + }) + .forEach((value) => { + const cipher = this.initLoginCipher(); + const url = this.getValueOrDefault(value.url, this.getValueOrDefault(value.hostname)); + cipher.name = this.getValueOrDefault(this.nameFromUrl(url), "--"); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(url); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/fsecureFskImporter.ts b/jslib/common/src/importers/fsecureFskImporter.ts new file mode 100644 index 00000000..c81a0820 --- /dev/null +++ b/jslib/common/src/importers/fsecureFskImporter.ts @@ -0,0 +1,59 @@ +import { CipherType } from "../enums/cipherType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class FSecureFskImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.data == null) { + result.success = false; + return Promise.resolve(result); + } + + for (const key in results.data) { + // eslint-disable-next-line + if (!results.data.hasOwnProperty(key)) { + continue; + } + + const value = results.data[key]; + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.service); + cipher.notes = this.getValueOrDefault(value.notes); + + if (value.style === "website" || value.style === "globe") { + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.url); + } else if (value.style === "creditcard") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(value.username); + cipher.card.number = this.getValueOrDefault(value.creditNumber); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = this.getValueOrDefault(value.creditCvv); + if (!this.isNullOrWhitespace(value.creditExpiry)) { + if (!this.setCardExpiration(cipher, value.creditExpiry)) { + this.processKvp(cipher, "Expiration", value.creditExpiry); + } + } + if (!this.isNullOrWhitespace(value.password)) { + this.processKvp(cipher, "PIN", value.password); + } + } else { + continue; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/gnomeJsonImporter.ts b/jslib/common/src/importers/gnomeJsonImporter.ts new file mode 100644 index 00000000..1dfac675 --- /dev/null +++ b/jslib/common/src/importers/gnomeJsonImporter.ts @@ -0,0 +1,71 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class GnomeJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || Object.keys(results).length === 0) { + result.success = false; + return Promise.resolve(result); + } + + for (const keyRing in results) { + if ( + !results.hasOwnProperty(keyRing) || // eslint-disable-line + this.isNullOrWhitespace(keyRing) || + results[keyRing].length === 0 + ) { + continue; + } + + results[keyRing].forEach((value: any) => { + if ( + this.isNullOrWhitespace(value.display_name) || + value.display_name.indexOf("http") !== 0 + ) { + return; + } + + this.processFolder(result, keyRing); + const cipher = this.initLoginCipher(); + cipher.name = value.display_name.replace("http://", "").replace("https://", ""); + if (cipher.name.length > 30) { + cipher.name = cipher.name.substring(0, 30); + } + cipher.login.password = this.getValueOrDefault(value.secret); + cipher.login.uris = this.makeUriArray(value.display_name); + + if (value.attributes != null) { + cipher.login.username = + value.attributes != null + ? this.getValueOrDefault(value.attributes.username_value) + : null; + for (const attr in value.attributes) { + if ( + !value.attributes.hasOwnProperty(attr) || // eslint-disable-line + attr === "username_value" || + attr === "xdg:schema" + ) { + continue; + } + this.processKvp(cipher, attr, value.attributes[attr]); + } + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/importError.ts b/jslib/common/src/importers/importError.ts new file mode 100644 index 00000000..6a74d66e --- /dev/null +++ b/jslib/common/src/importers/importError.ts @@ -0,0 +1,5 @@ +export class ImportError extends Error { + constructor(message?: string, public passwordRequired: boolean = false) { + super(message); + } +} diff --git a/jslib/common/src/importers/importer.ts b/jslib/common/src/importers/importer.ts new file mode 100644 index 00000000..a62cc6c1 --- /dev/null +++ b/jslib/common/src/importers/importer.ts @@ -0,0 +1,6 @@ +import { ImportResult } from "../models/domain/importResult"; + +export interface Importer { + organizationId: string; + parse(data: string): Promise; +} diff --git a/jslib/common/src/importers/kasperskyTxtImporter.ts b/jslib/common/src/importers/kasperskyTxtImporter.ts new file mode 100644 index 00000000..b049f2bb --- /dev/null +++ b/jslib/common/src/importers/kasperskyTxtImporter.ts @@ -0,0 +1,124 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +const NotesHeader = "Notes\n\n"; +const ApplicationsHeader = "Applications\n\n"; +const WebsitesHeader = "Websites\n\n"; +const Delimiter = "\n---\n"; + +export class KasperskyTxtImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + + let notesData: string; + let applicationsData: string; + let websitesData: string; + let workingData = this.splitNewLine(data).join("\n"); + + if (workingData.indexOf(NotesHeader) !== -1) { + const parts = workingData.split(NotesHeader); + if (parts.length > 1) { + workingData = parts[0]; + notesData = parts[1]; + } + } + if (workingData.indexOf(ApplicationsHeader) !== -1) { + const parts = workingData.split(ApplicationsHeader); + if (parts.length > 1) { + workingData = parts[0]; + applicationsData = parts[1]; + } + } + if (workingData.indexOf(WebsitesHeader) === 0) { + const parts = workingData.split(WebsitesHeader); + if (parts.length > 1) { + workingData = parts[0]; + websitesData = parts[1]; + } + } + + const notes = this.parseDataCategory(notesData); + const applications = this.parseDataCategory(applicationsData); + const websites = this.parseDataCategory(websitesData); + + notes.forEach((n) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(n.get("Name")); + cipher.notes = this.getValueOrDefault(n.get("Text")); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + websites.concat(applications).forEach((w) => { + const cipher = this.initLoginCipher(); + const nameKey = w.has("Website name") ? "Website name" : "Application"; + cipher.name = this.getValueOrDefault(w.get(nameKey), ""); + if (!this.isNullOrWhitespace(w.get("Login name"))) { + if (!this.isNullOrWhitespace(cipher.name)) { + cipher.name += ": "; + } + cipher.name += w.get("Login name"); + } + cipher.notes = this.getValueOrDefault(w.get("Comment")); + if (w.has("Website URL")) { + cipher.login.uris = this.makeUriArray(w.get("Website URL")); + } + cipher.login.username = this.getValueOrDefault(w.get("Login")); + cipher.login.password = this.getValueOrDefault(w.get("Password")); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + private parseDataCategory(data: string): Map[] { + if (this.isNullOrWhitespace(data) || data.indexOf(Delimiter) === -1) { + return []; + } + const items: Map[] = []; + data.split(Delimiter).forEach((p) => { + if (p.indexOf("\n") === -1) { + return; + } + const item = new Map(); + let itemComment: string; + let itemCommentKey: string; + p.split("\n").forEach((l) => { + if (itemComment != null) { + itemComment += "\n" + l; + return; + } + const colonIndex = l.indexOf(":"); + let key: string; + let val: string; + if (colonIndex === -1) { + return; + } else { + key = l.substring(0, colonIndex); + if (l.length > colonIndex + 1) { + val = l.substring(colonIndex + 2); + } + } + if (key != null) { + item.set(key, val); + } + if (key === "Comment" || key === "Text") { + itemComment = val; + itemCommentKey = key; + } + }); + if (itemComment != null && itemCommentKey != null) { + item.set(itemCommentKey, itemComment); + } + if (item.size === 0) { + return; + } + items.push(item); + }); + return items; + } +} diff --git a/jslib/common/src/importers/keepass2XmlImporter.ts b/jslib/common/src/importers/keepass2XmlImporter.ts new file mode 100644 index 00000000..2ed5d0f9 --- /dev/null +++ b/jslib/common/src/importers/keepass2XmlImporter.ts @@ -0,0 +1,101 @@ +import { FieldType } from "../enums/fieldType"; +import { ImportResult } from "../models/domain/importResult"; +import { FolderView } from "../models/view/folderView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class KeePass2XmlImporter extends BaseImporter implements Importer { + result = new ImportResult(); + + parse(data: string): Promise { + const doc = this.parseXml(data); + if (doc == null) { + this.result.success = false; + return Promise.resolve(this.result); + } + + const rootGroup = doc.querySelector("KeePassFile > Root > Group"); + if (rootGroup == null) { + this.result.errorMessage = "Missing `KeePassFile > Root > Group` node."; + this.result.success = false; + return Promise.resolve(this.result); + } + + this.traverse(rootGroup, true, ""); + + if (this.organization) { + this.moveFoldersToCollections(this.result); + } + + this.result.success = true; + return Promise.resolve(this.result); + } + + traverse(node: Element, isRootNode: boolean, groupPrefixName: string) { + const folderIndex = this.result.folders.length; + let groupName = groupPrefixName; + + if (!isRootNode) { + if (groupName !== "") { + groupName += "/"; + } + const nameEl = this.querySelectorDirectChild(node, "Name"); + groupName += nameEl == null ? "-" : nameEl.textContent; + const folder = new FolderView(); + folder.name = groupName; + this.result.folders.push(folder); + } + + this.querySelectorAllDirectChild(node, "Entry").forEach((entry) => { + const cipherIndex = this.result.ciphers.length; + + const cipher = this.initLoginCipher(); + this.querySelectorAllDirectChild(entry, "String").forEach((entryString) => { + const valueEl = this.querySelectorDirectChild(entryString, "Value"); + const value = valueEl != null ? valueEl.textContent : null; + if (this.isNullOrWhitespace(value)) { + return; + } + const keyEl = this.querySelectorDirectChild(entryString, "Key"); + const key = keyEl != null ? keyEl.textContent : null; + + if (key === "URL") { + cipher.login.uris = this.makeUriArray(value); + } else if (key === "UserName") { + cipher.login.username = value; + } else if (key === "Password") { + cipher.login.password = value; + } else if (key === "otp") { + cipher.login.totp = value.replace("key=", ""); + } else if (key === "Title") { + cipher.name = value; + } else if (key === "Notes") { + cipher.notes += value + "\n"; + } else { + let type = FieldType.Text; + const attrs = valueEl.attributes as any; + if ( + attrs.length > 0 && + attrs.ProtectInMemory != null && + attrs.ProtectInMemory.value === "True" + ) { + type = FieldType.Hidden; + } + this.processKvp(cipher, key, value, type); + } + }); + + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + + if (!isRootNode) { + this.result.folderRelationships.push([cipherIndex, folderIndex]); + } + }); + + this.querySelectorAllDirectChild(node, "Group").forEach((group) => { + this.traverse(group, false, groupName); + }); + } +} diff --git a/jslib/common/src/importers/keepassxCsvImporter.ts b/jslib/common/src/importers/keepassxCsvImporter.ts new file mode 100644 index 00000000..58640d5a --- /dev/null +++ b/jslib/common/src/importers/keepassxCsvImporter.ts @@ -0,0 +1,44 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class KeePassXCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (this.isNullOrWhitespace(value.Title)) { + return; + } + + value.Group = + !this.isNullOrWhitespace(value.Group) && value.Group.startsWith("Root/") + ? value.Group.replace("Root/", "") + : value.Group; + const groupName = !this.isNullOrWhitespace(value.Group) ? value.Group : null; + this.processFolder(result, groupName); + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value.Notes); + cipher.name = this.getValueOrDefault(value.Title, "--"); + cipher.login.username = this.getValueOrDefault(value.Username); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.URL); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/keeperImporters/keeperCsvImporter.ts b/jslib/common/src/importers/keeperImporters/keeperCsvImporter.ts new file mode 100644 index 00000000..e9c65d71 --- /dev/null +++ b/jslib/common/src/importers/keeperImporters/keeperCsvImporter.ts @@ -0,0 +1,45 @@ +import { ImportResult } from "../../models/domain/importResult"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +export class KeeperCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 6) { + return; + } + + this.processFolder(result, value[0]); + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value[5]) + "\n"; + cipher.name = this.getValueOrDefault(value[1], "--"); + cipher.login.username = this.getValueOrDefault(value[2]); + cipher.login.password = this.getValueOrDefault(value[3]); + cipher.login.uris = this.makeUriArray(value[4]); + + if (value.length > 7) { + // we have some custom fields. + for (let i = 7; i < value.length; i = i + 2) { + this.processKvp(cipher, value[i], value[i + 1]); + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/keeperImporters/keeperJsonImporter.ts b/jslib/common/src/importers/keeperImporters/keeperJsonImporter.ts new file mode 100644 index 00000000..7ee80b08 --- /dev/null +++ b/jslib/common/src/importers/keeperImporters/keeperJsonImporter.ts @@ -0,0 +1,69 @@ +import { ImportResult } from "../../models/domain/importResult"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { KeeperJsonExport, RecordsEntity } from "./types/keeperJsonTypes"; + +export class KeeperJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const keeperExport: KeeperJsonExport = JSON.parse(data); + if (keeperExport == null || keeperExport.records == null || keeperExport.records.length === 0) { + result.success = false; + return Promise.resolve(result); + } + + keeperExport.records.forEach((record) => { + this.parseFolders(result, record); + + const cipher = this.initLoginCipher(); + cipher.name = record.title; + cipher.login.username = record.login; + cipher.login.password = record.password; + + cipher.login.uris = this.makeUriArray(record.login_url); + cipher.notes = record.notes; + + if (record.custom_fields != null) { + let customfieldKeys = Object.keys(record.custom_fields); + if (record.custom_fields["TFC:Keeper"] != null) { + customfieldKeys = customfieldKeys.filter((item) => item !== "TFC:Keeper"); + cipher.login.totp = record.custom_fields["TFC:Keeper"]; + } + + customfieldKeys.forEach((key) => { + this.processKvp(cipher, key, record.custom_fields[key]); + }); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private parseFolders(result: ImportResult, record: RecordsEntity) { + if (record.folders == null || record.folders.length === 0) { + return; + } + + record.folders.forEach((item) => { + if (item.folder != null) { + this.processFolder(result, item.folder); + return; + } + + if (item.shared_folder != null) { + this.processFolder(result, item.shared_folder); + return; + } + }); + } +} diff --git a/jslib/common/src/importers/keeperImporters/types/keeperJsonTypes.ts b/jslib/common/src/importers/keeperImporters/types/keeperJsonTypes.ts new file mode 100644 index 00000000..1f6d2ea4 --- /dev/null +++ b/jslib/common/src/importers/keeperImporters/types/keeperJsonTypes.ts @@ -0,0 +1,41 @@ +export interface KeeperJsonExport { + shared_folders?: SharedFoldersEntity[] | null; + records?: RecordsEntity[] | null; +} + +export interface SharedFoldersEntity { + path: string; + manage_users: boolean; + manage_records: boolean; + can_edit: boolean; + can_share: boolean; + permissions?: PermissionsEntity[] | null; +} + +export interface PermissionsEntity { + uid?: string | null; + manage_users: boolean; + manage_records: boolean; + name?: string | null; +} + +export interface RecordsEntity { + title: string; + login: string; + password: string; + login_url: string; + notes?: string; + custom_fields?: CustomFields; + folders?: FoldersEntity[] | null; +} + +export type CustomFields = { + [key: string]: string | null; +}; + +export interface FoldersEntity { + folder?: string | null; + shared_folder?: string | null; + can_edit?: boolean | null; + can_share?: boolean | null; +} diff --git a/jslib/common/src/importers/lastpassCsvImporter.ts b/jslib/common/src/importers/lastpassCsvImporter.ts new file mode 100644 index 00000000..5c016028 --- /dev/null +++ b/jslib/common/src/importers/lastpassCsvImporter.ts @@ -0,0 +1,285 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { CipherView } from "../models/view/cipherView"; +import { FolderView } from "../models/view/folderView"; +import { IdentityView } from "../models/view/identityView"; +import { LoginView } from "../models/view/loginView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class LastPassCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipherIndex = result.ciphers.length; + let folderIndex = result.folders.length; + let grouping = value.grouping; + if (grouping != null) { + // eslint-disable-next-line + grouping = grouping.replace(/\\/g, "/").replace(/[\x00-\x1F\x7F-\x9F]/g, ""); + } + const hasFolder = this.getValueOrDefault(grouping, "(none)") !== "(none)"; + let addFolder = hasFolder; + + if (hasFolder) { + for (let i = 0; i < result.folders.length; i++) { + if (result.folders[i].name === grouping) { + addFolder = false; + folderIndex = i; + break; + } + } + } + + const cipher = this.buildBaseCipher(value); + if (cipher.type === CipherType.Login) { + cipher.notes = this.getValueOrDefault(value.extra); + cipher.login = new LoginView(); + cipher.login.uris = this.makeUriArray(value.url); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.totp = this.getValueOrDefault(value.totp); + } else if (cipher.type === CipherType.SecureNote) { + this.parseSecureNote(value, cipher); + } else if (cipher.type === CipherType.Card) { + cipher.card = this.parseCard(value); + cipher.notes = this.getValueOrDefault(value.notes); + } else if (cipher.type === CipherType.Identity) { + cipher.identity = this.parseIdentity(value); + cipher.notes = this.getValueOrDefault(value.notes); + if (!this.isNullOrWhitespace(value.ccnum)) { + // there is a card on this identity too + const cardCipher = this.buildBaseCipher(value); + cardCipher.identity = null; + cardCipher.type = CipherType.Card; + cardCipher.card = this.parseCard(value); + result.ciphers.push(cardCipher); + } + } + + result.ciphers.push(cipher); + + if (addFolder) { + const f = new FolderView(); + f.name = grouping; + result.folders.push(f); + } + if (hasFolder) { + result.folderRelationships.push([cipherIndex, folderIndex]); + } + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private buildBaseCipher(value: any) { + const cipher = new CipherView(); + // eslint-disable-next-line + if (value.hasOwnProperty("profilename") && value.hasOwnProperty("profilelanguage")) { + // form fill + cipher.favorite = false; + cipher.name = this.getValueOrDefault(value.profilename, "--"); + cipher.type = CipherType.Card; + + if ( + !this.isNullOrWhitespace(value.title) || + !this.isNullOrWhitespace(value.firstname) || + !this.isNullOrWhitespace(value.lastname) || + !this.isNullOrWhitespace(value.address1) || + !this.isNullOrWhitespace(value.phone) || + !this.isNullOrWhitespace(value.username) || + !this.isNullOrWhitespace(value.email) + ) { + cipher.type = CipherType.Identity; + } + } else { + // site or secure note + cipher.favorite = !this.organization && this.getValueOrDefault(value.fav, "0") === "1"; + cipher.name = this.getValueOrDefault(value.name, "--"); + cipher.type = value.url === "http://sn" ? CipherType.SecureNote : CipherType.Login; + } + return cipher; + } + + private parseCard(value: any): CardView { + const card = new CardView(); + card.cardholderName = this.getValueOrDefault(value.ccname); + card.number = this.getValueOrDefault(value.ccnum); + card.code = this.getValueOrDefault(value.cccsc); + card.brand = this.getCardBrand(value.ccnum); + + if (!this.isNullOrWhitespace(value.ccexp) && value.ccexp.indexOf("-") > -1) { + const ccexpParts = (value.ccexp as string).split("-"); + if (ccexpParts.length > 1) { + card.expYear = ccexpParts[0]; + card.expMonth = ccexpParts[1]; + if (card.expMonth.length === 2 && card.expMonth[0] === "0") { + card.expMonth = card.expMonth[1]; + } + } + } + + return card; + } + + private parseIdentity(value: any): IdentityView { + const identity = new IdentityView(); + identity.title = this.getValueOrDefault(value.title); + identity.firstName = this.getValueOrDefault(value.firstname); + identity.middleName = this.getValueOrDefault(value.middlename); + identity.lastName = this.getValueOrDefault(value.lastname); + identity.username = this.getValueOrDefault(value.username); + identity.company = this.getValueOrDefault(value.company); + identity.ssn = this.getValueOrDefault(value.ssn); + identity.address1 = this.getValueOrDefault(value.address1); + identity.address2 = this.getValueOrDefault(value.address2); + identity.address3 = this.getValueOrDefault(value.address3); + identity.city = this.getValueOrDefault(value.city); + identity.state = this.getValueOrDefault(value.state); + identity.postalCode = this.getValueOrDefault(value.zip); + identity.country = this.getValueOrDefault(value.country); + identity.email = this.getValueOrDefault(value.email); + identity.phone = this.getValueOrDefault(value.phone); + + if (!this.isNullOrWhitespace(identity.title)) { + identity.title = identity.title.charAt(0).toUpperCase() + identity.title.slice(1); + } + + return identity; + } + + private parseSecureNote(value: any, cipher: CipherView) { + const extraParts = this.splitNewLine(value.extra); + let processedNote = false; + + if (extraParts.length) { + const typeParts = extraParts[0].split(":"); + if ( + typeParts.length > 1 && + typeParts[0] === "NoteType" && + (typeParts[1] === "Credit Card" || typeParts[1] === "Address") + ) { + if (typeParts[1] === "Credit Card") { + const mappedData = this.parseSecureNoteMapping(cipher, extraParts, { + Number: "number", + "Name on Card": "cardholderName", + "Security Code": "code", + // LP provides date in a format like 'June,2020' + // Store in expMonth, then parse and modify + "Expiration Date": "expMonth", + }); + + if (this.isNullOrWhitespace(mappedData.expMonth) || mappedData.expMonth === ",") { + // No expiration data + mappedData.expMonth = undefined; + } else { + const [monthString, year] = mappedData.expMonth.split(","); + // Parse month name into number + if (!this.isNullOrWhitespace(monthString)) { + const month = new Date(Date.parse(monthString.trim() + " 1, 2012")).getMonth() + 1; + if (isNaN(month)) { + mappedData.expMonth = undefined; + } else { + mappedData.expMonth = month.toString(); + } + } else { + mappedData.expMonth = undefined; + } + if (!this.isNullOrWhitespace(year)) { + mappedData.expYear = year; + } + } + + cipher.type = CipherType.Card; + cipher.card = mappedData; + } else if (typeParts[1] === "Address") { + const mappedData = this.parseSecureNoteMapping(cipher, extraParts, { + Title: "title", + "First Name": "firstName", + "Last Name": "lastName", + "Middle Name": "middleName", + Company: "company", + "Address 1": "address1", + "Address 2": "address2", + "Address 3": "address3", + "City / Town": "city", + State: "state", + "Zip / Postal Code": "postalCode", + Country: "country", + "Email Address": "email", + Username: "username", + }); + cipher.type = CipherType.Identity; + cipher.identity = mappedData; + } + processedNote = true; + } + } + + if (!processedNote) { + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + cipher.notes = this.getValueOrDefault(value.extra); + } + } + + private parseSecureNoteMapping(cipher: CipherView, extraParts: string[], map: any): T { + const dataObj: any = {}; + + let processingNotes = false; + extraParts.forEach((extraPart) => { + let key: string = null; + let val: string = null; + if (!processingNotes) { + if (this.isNullOrWhitespace(extraPart)) { + return; + } + const colonIndex = extraPart.indexOf(":"); + if (colonIndex === -1) { + key = extraPart; + } else { + key = extraPart.substring(0, colonIndex); + if (extraPart.length > colonIndex) { + val = extraPart.substring(colonIndex + 1); + } + } + if (this.isNullOrWhitespace(key) || this.isNullOrWhitespace(val) || key === "NoteType") { + return; + } + } + + if (processingNotes) { + cipher.notes += "\n" + extraPart; + } else if (key === "Notes") { + if (!this.isNullOrWhitespace(cipher.notes)) { + cipher.notes += "\n" + val; + } else { + cipher.notes = val; + } + processingNotes = true; + // eslint-disable-next-line + } else if (map.hasOwnProperty(key)) { + dataObj[map[key]] = val; + } else { + this.processKvp(cipher, key, val); + } + }); + + return dataObj; + } +} diff --git a/jslib/common/src/importers/logMeOnceCsvImporter.ts b/jslib/common/src/importers/logMeOnceCsvImporter.ts new file mode 100644 index 00000000..698ebc80 --- /dev/null +++ b/jslib/common/src/importers/logMeOnceCsvImporter.ts @@ -0,0 +1,31 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class LogMeOnceCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 4) { + return; + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[0], "--"); + cipher.login.username = this.getValueOrDefault(value[2]); + cipher.login.password = this.getValueOrDefault(value[3]); + cipher.login.uris = this.makeUriArray(value[1]); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/meldiumCsvImporter.ts b/jslib/common/src/importers/meldiumCsvImporter.ts new file mode 100644 index 00000000..79835a90 --- /dev/null +++ b/jslib/common/src/importers/meldiumCsvImporter.ts @@ -0,0 +1,29 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class MeldiumCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.DisplayName, "--"); + cipher.notes = this.getValueOrDefault(value.Notes); + cipher.login.username = this.getValueOrDefault(value.UserName); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.Url); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/msecureCsvImporter.ts b/jslib/common/src/importers/msecureCsvImporter.ts new file mode 100644 index 00000000..583d13d7 --- /dev/null +++ b/jslib/common/src/importers/msecureCsvImporter.ts @@ -0,0 +1,61 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class MSecureCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 3) { + return; + } + + const folderName = + this.getValueOrDefault(value[0], "Unassigned") !== "Unassigned" ? value[0] : null; + this.processFolder(result, folderName); + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[2], "--"); + + if (value[1] === "Web Logins" || value[1] === "Login") { + cipher.login.uris = this.makeUriArray(value[4]); + cipher.login.username = this.getValueOrDefault(value[5]); + cipher.login.password = this.getValueOrDefault(value[6]); + cipher.notes = !this.isNullOrWhitespace(value[3]) ? value[3].split("\\n").join("\n") : null; + } else if (value.length > 3) { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + for (let i = 3; i < value.length; i++) { + if (!this.isNullOrWhitespace(value[i])) { + cipher.notes += value[i] + "\n"; + } + } + } + + if (!this.isNullOrWhitespace(value[1]) && cipher.type !== CipherType.Login) { + cipher.name = value[1] + ": " + cipher.name; + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/mykiCsvImporter.ts b/jslib/common/src/importers/mykiCsvImporter.ts new file mode 100644 index 00000000..d9a05e1e --- /dev/null +++ b/jslib/common/src/importers/mykiCsvImporter.ts @@ -0,0 +1,157 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { CipherView } from "../models/view/cipherView"; +import { IdentityView } from "../models/view/identityView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +const mappedBaseColumns = ["nickname", "additionalInfo"]; +const _mappedUserAccountColumns = new Set( + mappedBaseColumns.concat(["url", "username", "password", "twofaSecret"]) +); +const _mappedCreditCardColumns = new Set( + mappedBaseColumns.concat(["cardNumber", "cardName", "exp_month", "exp_year", "cvv"]) +); + +const _mappedIdentityColumns = new Set( + mappedBaseColumns.concat([ + "title", + "firstName", + "middleName", + "lastName", + "email", + "firstAddressLine", + "secondAddressLine", + "city", + "country", + "zipCode", + ]) +); + +const _mappedIdCardColumns = new Set(mappedBaseColumns.concat(["idName", "idNumber", "idCountry"])); + +const _mappedTwoFaColumns = new Set(mappedBaseColumns.concat(["authToken"])); + +const _mappedUserNoteColumns = new Set(mappedBaseColumns.concat(["content"])); + +export class MykiCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.nickname, "--"); + cipher.notes = this.getValueOrDefault(value.additionalInfo); + + if (value.url !== undefined) { + // Accounts + cipher.login.uris = this.makeUriArray(value.url); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.totp = this.getValueOrDefault(value.twofaSecret); + + this.importUnmappedFields(cipher, value, _mappedUserAccountColumns); + } else if (value.authToken !== undefined) { + // TwoFA + cipher.login.totp = this.getValueOrDefault(value.authToken); + + this.importUnmappedFields(cipher, value, _mappedTwoFaColumns); + } else if (value.cardNumber !== undefined) { + // Cards + cipher.card = new CardView(); + cipher.type = CipherType.Card; + cipher.card.cardholderName = this.getValueOrDefault(value.cardName); + cipher.card.number = this.getValueOrDefault(value.cardNumber); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.expMonth = this.getValueOrDefault(value.exp_month); + cipher.card.expYear = this.getValueOrDefault(value.exp_year); + cipher.card.code = this.getValueOrDefault(value.cvv); + + this.importUnmappedFields(cipher, value, _mappedCreditCardColumns); + } else if (value.firstName !== undefined) { + // Identities + cipher.identity = new IdentityView(); + cipher.type = CipherType.Identity; + cipher.identity.title = this.getValueOrDefault(value.title); + cipher.identity.firstName = this.getValueOrDefault(value.firstName); + cipher.identity.middleName = this.getValueOrDefault(value.middleName); + cipher.identity.lastName = this.getValueOrDefault(value.lastName); + cipher.identity.phone = this.getValueOrDefault(value.number); + cipher.identity.email = this.getValueOrDefault(value.email); + cipher.identity.address1 = this.getValueOrDefault(value.firstAddressLine); + cipher.identity.address2 = this.getValueOrDefault(value.secondAddressLine); + cipher.identity.city = this.getValueOrDefault(value.city); + cipher.identity.country = this.getValueOrDefault(value.country); + cipher.identity.postalCode = this.getValueOrDefault(value.zipCode); + + this.importUnmappedFields(cipher, value, _mappedIdentityColumns); + } else if (value.idType !== undefined) { + // IdCards + + cipher.identity = new IdentityView(); + cipher.type = CipherType.Identity; + this.processFullName(cipher, value.idName); + cipher.identity.country = this.getValueOrDefault(value.idCountry); + + switch (value.idType) { + // case "Driver's License": + // case "ID Card": + // case "Outdoor License": + // case "Software License": + // case "Tax Number": + // case "Bank Account": + // case "Insurance Card": + // case "Health Card": + // case "Membership": + // case "Database": + // case "Reward Program": + // case "Tour Visa": + case "Passport": + cipher.identity.passportNumber = value.idNumber; + break; + case "Social Security": + cipher.identity.ssn = value.idNumber; + break; + default: + cipher.identity.licenseNumber = value.idNumber; + break; + } + + this.importUnmappedFields(cipher, value, _mappedIdCardColumns); + } else if (value.content !== undefined) { + // Notes + cipher.secureNote = new SecureNoteView(); + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + cipher.notes = this.getValueOrDefault(value.content); + + this.importUnmappedFields(cipher, value, _mappedUserNoteColumns); + } else { + return; + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + importUnmappedFields(cipher: CipherView, row: any, mappedValues: Set) { + const unmappedFields = Object.keys(row).filter((x) => !mappedValues.has(x)); + unmappedFields.forEach((key) => { + const item = row as any; + this.processKvp(cipher, key, item[key]); + }); + } +} diff --git a/jslib/common/src/importers/nordpassCsvImporter.ts b/jslib/common/src/importers/nordpassCsvImporter.ts new file mode 100644 index 00000000..225e7ec2 --- /dev/null +++ b/jslib/common/src/importers/nordpassCsvImporter.ts @@ -0,0 +1,127 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; +import { LoginView } from "../models/view/loginView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +type nodePassCsvParsed = { + name: string; + url: string; + username: string; + password: string; + note: string; + cardholdername: string; + cardnumber: string; + cvc: string; + expirydate: string; + zipcode: string; + folder: string; + full_name: string; + phone_number: string; + email: string; + address1: string; + address2: string; + city: string; + country: string; + state: string; +}; + +export class NordPassCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results: nodePassCsvParsed[] = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((record) => { + const recordType = this.evaluateType(record); + if (recordType === undefined) { + return; + } + + if (!this.organization) { + this.processFolder(result, record.folder); + } + + const cipher = new CipherView(); + cipher.name = this.getValueOrDefault(record.name, "--"); + cipher.notes = this.getValueOrDefault(record.note); + + switch (recordType) { + case CipherType.Login: + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.username = this.getValueOrDefault(record.username); + cipher.login.password = this.getValueOrDefault(record.password); + cipher.login.uris = this.makeUriArray(record.url); + break; + case CipherType.Card: + cipher.type = CipherType.Card; + cipher.card.cardholderName = this.getValueOrDefault(record.cardholdername); + cipher.card.number = this.getValueOrDefault(record.cardnumber); + cipher.card.code = this.getValueOrDefault(record.cvc); + cipher.card.brand = this.getCardBrand(cipher.card.number); + this.setCardExpiration(cipher, record.expirydate); + break; + + case CipherType.Identity: + cipher.type = CipherType.Identity; + + this.processFullName(cipher, this.getValueOrDefault(record.full_name)); + cipher.identity.address1 = this.getValueOrDefault(record.address1); + cipher.identity.address2 = this.getValueOrDefault(record.address2); + cipher.identity.city = this.getValueOrDefault(record.city); + cipher.identity.state = this.getValueOrDefault(record.state); + cipher.identity.postalCode = this.getValueOrDefault(record.zipcode); + cipher.identity.country = this.getValueOrDefault(record.country); + if (cipher.identity.country != null) { + cipher.identity.country = cipher.identity.country.toUpperCase(); + } + cipher.identity.email = this.getValueOrDefault(record.email); + cipher.identity.phone = this.getValueOrDefault(record.phone_number); + break; + case CipherType.SecureNote: + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + break; + default: + break; + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private evaluateType(record: nodePassCsvParsed): CipherType { + if (!this.isNullOrWhitespace(record.username)) { + return CipherType.Login; + } + + if (!this.isNullOrWhitespace(record.cardnumber)) { + return CipherType.Card; + } + + if (!this.isNullOrWhitespace(record.full_name)) { + return CipherType.Identity; + } + + if (!this.isNullOrWhitespace(record.note)) { + return CipherType.SecureNote; + } + + return undefined; + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/cipherImportContext.ts b/jslib/common/src/importers/onepasswordImporters/cipherImportContext.ts new file mode 100644 index 00000000..560f5c01 --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/cipherImportContext.ts @@ -0,0 +1,8 @@ +import { CipherView } from "../../models/view/cipherView"; + +export class CipherImportContext { + lowerProperty: string; + constructor(public importRecord: any, public property: string, public cipher: CipherView) { + this.lowerProperty = property.toLowerCase(); + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepassword1PifImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepassword1PifImporter.ts new file mode 100644 index 00000000..f261140e --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepassword1PifImporter.ts @@ -0,0 +1,276 @@ +import { CipherType } from "../../enums/cipherType"; +import { FieldType } from "../../enums/fieldType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { PasswordHistoryView } from "../../models/view/passwordHistoryView"; +import { SecureNoteView } from "../../models/view/secureNoteView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +export class OnePassword1PifImporter extends BaseImporter implements Importer { + result = new ImportResult(); + + parse(data: string): Promise { + data.split(this.newLineRegex).forEach((line) => { + if (this.isNullOrWhitespace(line) || line[0] !== "{") { + return; + } + const item = JSON.parse(line); + if (item.trashed === true) { + return; + } + const cipher = this.initLoginCipher(); + + if (this.isNullOrWhitespace(item.hmac)) { + this.processStandardItem(item, cipher); + } else { + this.processWinOpVaultItem(item, cipher); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + + this.result.success = true; + return Promise.resolve(this.result); + } + + private processWinOpVaultItem(item: any, cipher: CipherView) { + if (item.overview != null) { + cipher.name = this.getValueOrDefault(item.overview.title); + if (item.overview.URLs != null) { + const urls: string[] = []; + item.overview.URLs.forEach((url: any) => { + if (!this.isNullOrWhitespace(url.u)) { + urls.push(url.u); + } + }); + cipher.login.uris = this.makeUriArray(urls); + } + } + + if (item.details != null) { + if (item.details.passwordHistory != null) { + this.parsePasswordHistory(item.details.passwordHistory, cipher); + } + if ( + !this.isNullOrWhitespace(item.details.ccnum) || + !this.isNullOrWhitespace(item.details.cvv) + ) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } else if ( + !this.isNullOrWhitespace(item.details.firstname) || + !this.isNullOrWhitespace(item.details.address1) + ) { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + } + if (cipher.type === CipherType.Login && !this.isNullOrWhitespace(item.details.password)) { + cipher.login.password = item.details.password; + } + if (!this.isNullOrWhitespace(item.details.notesPlain)) { + cipher.notes = item.details.notesPlain.split(this.newLineRegex).join("\n") + "\n"; + } + if (item.details.fields != null) { + this.parseFields(item.details.fields, cipher, "designation", "value", "name"); + } + if (item.details.sections != null) { + item.details.sections.forEach((section: any) => { + if (section.fields != null) { + this.parseFields(section.fields, cipher, "n", "v", "t"); + } + }); + } + } + } + + private processStandardItem(item: any, cipher: CipherView) { + cipher.favorite = item.openContents && item.openContents.faveIndex ? true : false; + cipher.name = this.getValueOrDefault(item.title); + + if (item.typeName === "securenotes.SecureNote") { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + } else if (item.typeName === "wallet.financial.CreditCard") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } else if (item.typeName === "identities.Identity") { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + } else { + cipher.login.uris = this.makeUriArray(item.location); + } + + if (item.secureContents != null) { + if (item.secureContents.passwordHistory != null) { + this.parsePasswordHistory(item.secureContents.passwordHistory, cipher); + } + if (!this.isNullOrWhitespace(item.secureContents.notesPlain)) { + cipher.notes = item.secureContents.notesPlain.split(this.newLineRegex).join("\n") + "\n"; + } + if (cipher.type === CipherType.Login) { + if (!this.isNullOrWhitespace(item.secureContents.password)) { + cipher.login.password = item.secureContents.password; + } + if (item.secureContents.URLs != null) { + const urls: string[] = []; + item.secureContents.URLs.forEach((u: any) => { + if (!this.isNullOrWhitespace(u.url)) { + urls.push(u.url); + } + }); + if (urls.length > 0) { + cipher.login.uris = this.makeUriArray(urls); + } + } + } + if (item.secureContents.fields != null) { + this.parseFields(item.secureContents.fields, cipher, "designation", "value", "name"); + } + if (item.secureContents.sections != null) { + item.secureContents.sections.forEach((section: any) => { + if (section.fields != null) { + this.parseFields(section.fields, cipher, "n", "v", "t"); + } + }); + } + } + } + + private parsePasswordHistory(items: any[], cipher: CipherView) { + const maxSize = items.length > 5 ? 5 : items.length; + cipher.passwordHistory = items + .filter((h: any) => !this.isNullOrWhitespace(h.value) && h.time != null) + .sort((a, b) => b.time - a.time) + .slice(0, maxSize) + .map((h: any) => { + const ph = new PasswordHistoryView(); + ph.password = h.value; + ph.lastUsedDate = new Date(("" + h.time).length >= 13 ? h.time : h.time * 1000); + return ph; + }); + } + + private parseFields( + fields: any[], + cipher: CipherView, + designationKey: string, + valueKey: string, + nameKey: string + ) { + fields.forEach((field: any) => { + if (field[valueKey] == null || field[valueKey].toString().trim() === "") { + return; + } + + // TODO: when date FieldType exists, store this as a date field type instead of formatted Text if k is 'date' + const fieldValue = + field.k === "date" + ? new Date(field[valueKey] * 1000).toUTCString() + : field[valueKey].toString(); + const fieldDesignation = + field[designationKey] != null ? field[designationKey].toString() : null; + + if (cipher.type === CipherType.Login) { + if (this.isNullOrWhitespace(cipher.login.username) && fieldDesignation === "username") { + cipher.login.username = fieldValue; + return; + } else if ( + this.isNullOrWhitespace(cipher.login.password) && + fieldDesignation === "password" + ) { + cipher.login.password = fieldValue; + return; + } else if ( + this.isNullOrWhitespace(cipher.login.totp) && + fieldDesignation != null && + fieldDesignation.startsWith("TOTP_") + ) { + cipher.login.totp = fieldValue; + return; + } + } else if (cipher.type === CipherType.Card) { + if (this.isNullOrWhitespace(cipher.card.number) && fieldDesignation === "ccnum") { + cipher.card.number = fieldValue; + cipher.card.brand = this.getCardBrand(fieldValue); + return; + } else if (this.isNullOrWhitespace(cipher.card.code) && fieldDesignation === "cvv") { + cipher.card.code = fieldValue; + return; + } else if ( + this.isNullOrWhitespace(cipher.card.cardholderName) && + fieldDesignation === "cardholder" + ) { + cipher.card.cardholderName = fieldValue; + return; + } else if ( + this.isNullOrWhitespace(cipher.card.expiration) && + fieldDesignation === "expiry" && + fieldValue.length === 6 + ) { + cipher.card.expMonth = (fieldValue as string).substr(4, 2); + if (cipher.card.expMonth[0] === "0") { + cipher.card.expMonth = cipher.card.expMonth.substr(1, 1); + } + cipher.card.expYear = (fieldValue as string).substr(0, 4); + return; + } else if (fieldDesignation === "type") { + // Skip since brand was determined from number above + return; + } + } else if (cipher.type === CipherType.Identity) { + const identity = cipher.identity; + if (this.isNullOrWhitespace(identity.firstName) && fieldDesignation === "firstname") { + identity.firstName = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.lastName) && fieldDesignation === "lastname") { + identity.lastName = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.middleName) && fieldDesignation === "initial") { + identity.middleName = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.phone) && fieldDesignation === "defphone") { + identity.phone = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.company) && fieldDesignation === "company") { + identity.company = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.email) && fieldDesignation === "email") { + identity.email = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.username) && fieldDesignation === "username") { + identity.username = fieldValue; + return; + } else if (fieldDesignation === "address") { + // fieldValue is an object casted into a string, so access the plain value instead + const { street, city, country, zip } = field[valueKey]; + identity.address1 = this.getValueOrDefault(street); + identity.city = this.getValueOrDefault(city); + if (!this.isNullOrWhitespace(country)) { + identity.country = country.toUpperCase(); + } + identity.postalCode = this.getValueOrDefault(zip); + return; + } + } + + const fieldName = this.isNullOrWhitespace(field[nameKey]) ? "no_name" : field[nameKey]; + if ( + fieldName === "password" && + cipher.passwordHistory != null && + cipher.passwordHistory.some((h) => h.password === fieldValue) + ) { + return; + } + + const fieldType = field.k === "concealed" ? FieldType.Hidden : FieldType.Text; + this.processKvp(cipher, fieldName, fieldValue, fieldType); + }); + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts new file mode 100644 index 00000000..67f4bb25 --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts @@ -0,0 +1,637 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { FieldType } from "../../enums/fieldType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { LoginView } from "../../models/view/loginView"; +import { PasswordHistoryView } from "../../models/view/passwordHistoryView"; +import { SecureNoteView } from "../../models/view/secureNoteView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { + CategoryEnum, + Details, + ExportData, + FieldsEntity, + Item, + LoginFieldTypeEnum, + Overview, + PasswordHistoryEntity, + SectionsEntity, + UrlsEntity, + Value, + VaultsEntity, +} from "./types/onepassword1PuxImporterTypes"; + +export class OnePassword1PuxImporter extends BaseImporter implements Importer { + result = new ImportResult(); + + parse(data: string): Promise { + const exportData: ExportData = JSON.parse(data); + + const account = exportData.accounts[0]; + // TODO Add handling of multiple vaults + // const personalVaults = account.vaults[0].filter((v) => v.attrs.type === VaultAttributeTypeEnum.Personal); + account.vaults.forEach((vault: VaultsEntity) => { + vault.items.forEach((item: Item) => { + if (item.trashed === true) { + return; + } + + const cipher = this.initLoginCipher(); + + const category = item.categoryUuid as CategoryEnum; + switch (category) { + case CategoryEnum.Login: + case CategoryEnum.Database: + case CategoryEnum.Password: + case CategoryEnum.WirelessRouter: + case CategoryEnum.Server: + case CategoryEnum.API_Credential: + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + break; + case CategoryEnum.CreditCard: + case CategoryEnum.BankAccount: + cipher.type = CipherType.Card; + cipher.card = new CardView(); + break; + case CategoryEnum.SecureNote: + case CategoryEnum.SoftwareLicense: + case CategoryEnum.EmailAccount: + case CategoryEnum.MedicalRecord: + // case CategoryEnum.Document: + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + break; + case CategoryEnum.Identity: + case CategoryEnum.DriversLicense: + case CategoryEnum.OutdoorLicense: + case CategoryEnum.Membership: + case CategoryEnum.Passport: + case CategoryEnum.RewardsProgram: + case CategoryEnum.SocialSecurityNumber: + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + break; + default: + break; + } + + cipher.favorite = item.favIndex === 1 ? true : false; + + this.processOverview(item.overview, cipher); + + this.processLoginFields(item, cipher); + + this.processDetails(category, item.details, cipher); + + this.parsePasswordHistory(item.details.passwordHistory, cipher); + + this.processSections(category, item.details.sections, cipher); + + if (!this.isNullOrWhitespace(item.details.notesPlain)) { + cipher.notes = item.details.notesPlain.split(this.newLineRegex).join("\n") + "\n"; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + }); + + if (this.organization) { + this.moveFoldersToCollections(this.result); + } + + this.result.success = true; + return Promise.resolve(this.result); + } + + private processOverview(overview: Overview, cipher: CipherView) { + if (overview == null) { + return; + } + + cipher.name = this.getValueOrDefault(overview.title); + + if (overview.urls != null) { + const urls: string[] = []; + overview.urls.forEach((url: UrlsEntity) => { + if (!this.isNullOrWhitespace(url.url)) { + urls.push(url.url); + } + }); + cipher.login.uris = this.makeUriArray(urls); + } + + if (overview.tags != null && overview.tags.length > 0) { + const folderName = this.capitalize(overview.tags[0]); + this.processFolder(this.result, folderName); + } + } + + private capitalize(inputString: string): string { + return inputString.trim().replace(/\w\S*/g, (w) => w.replace(/^\w/, (c) => c.toUpperCase())); + } + + private processLoginFields(item: Item, cipher: CipherView) { + if (item.details == null) { + return; + } + + if (item.details.loginFields == null || item.details.loginFields.length === 0) { + return; + } + + item.details.loginFields.forEach((loginField) => { + if (loginField.designation === "username" && loginField.value !== "") { + cipher.type = CipherType.Login; + cipher.login.username = loginField.value; + return; + } + + if (loginField.designation === "password" && loginField.value !== "") { + cipher.type = CipherType.Login; + cipher.login.password = loginField.value; + return; + } + + let fieldValue = loginField.value; + let fieldType: FieldType = FieldType.Text; + switch (loginField.fieldType) { + case LoginFieldTypeEnum.Password: + fieldType = FieldType.Hidden; + break; + case LoginFieldTypeEnum.CheckBox: + fieldValue = loginField.value !== "" ? "true" : "false"; + fieldType = FieldType.Boolean; + break; + default: + break; + } + this.processKvp(cipher, loginField.name, fieldValue, fieldType); + }); + } + + private processDetails(category: CategoryEnum, details: Details, cipher: CipherView) { + if (category !== CategoryEnum.Password) { + return; + } + + if (details == null) { + return; + } + cipher.login.password = details.password; + } + + private processSections(category: CategoryEnum, sections: SectionsEntity[], cipher: CipherView) { + if (sections == null || sections.length === 0) { + return; + } + + sections.forEach((section: SectionsEntity) => { + if (section.fields == null) { + return; + } + + this.parseSectionFields(category, section.fields, cipher); + }); + } + + private parseSectionFields(category: CategoryEnum, fields: FieldsEntity[], cipher: CipherView) { + fields.forEach((field: FieldsEntity) => { + const valueKey = Object.keys(field.value)[0]; + const anyField = field as any; + + if ( + anyField.value == null || + anyField.value[valueKey] == null || + anyField.value[valueKey] === "" + ) { + return; + } + + const fieldName = this.getFieldName(field.id, field.title); + const fieldValue = this.extractValue(field.value, valueKey); + + if (cipher.type === CipherType.Login) { + if (this.fillLogin(field, fieldValue, cipher)) { + return; + } + + switch (category) { + case CategoryEnum.Login: + case CategoryEnum.Database: + case CategoryEnum.EmailAccount: + case CategoryEnum.WirelessRouter: + break; + + case CategoryEnum.Server: + if (this.isNullOrWhitespace(cipher.login.uri) && field.id === "url") { + cipher.login.uris = this.makeUriArray(fieldValue); + return; + } + break; + + case CategoryEnum.API_Credential: + if (this.fillApiCredentials(field, fieldValue, cipher)) { + return; + } + break; + default: + break; + } + } else if (cipher.type === CipherType.Card) { + if (this.fillCreditCard(field, fieldValue, cipher)) { + return; + } + + if (category === CategoryEnum.BankAccount) { + if (this.fillBankAccount(field, fieldValue, cipher)) { + return; + } + } + } else if (cipher.type === CipherType.Identity) { + if (this.fillIdentity(field, fieldValue, cipher, valueKey)) { + return; + } + if (valueKey === "address") { + // fieldValue is an object casted into a string, so access the plain value instead + const { street, city, country, zip, state } = field.value.address; + cipher.identity.address1 = this.getValueOrDefault(street); + cipher.identity.city = this.getValueOrDefault(city); + if (!this.isNullOrWhitespace(country)) { + cipher.identity.country = country.toUpperCase(); + } + cipher.identity.postalCode = this.getValueOrDefault(zip); + cipher.identity.state = this.getValueOrDefault(state); + return; + } + + switch (category) { + case CategoryEnum.Identity: + break; + case CategoryEnum.DriversLicense: + if (this.fillDriversLicense(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.OutdoorLicense: + if (this.fillOutdoorLicense(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.Membership: + if (this.fillMembership(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.Passport: + if (this.fillPassport(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.RewardsProgram: + if (this.fillRewardsProgram(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.SocialSecurityNumber: + if (this.fillSSN(field, fieldValue, cipher)) { + return; + } + break; + default: + break; + } + } + + if (valueKey === "email") { + // fieldValue is an object casted into a string, so access the plain value instead + const { email_address, provider } = field.value.email; + this.processKvp(cipher, fieldName, email_address, FieldType.Text); + this.processKvp(cipher, "provider", provider, FieldType.Text); + return; + } + + // Do not include a password field if it's already in the history + if ( + field.title === "password" && + cipher.passwordHistory != null && + cipher.passwordHistory.some((h) => h.password === fieldValue) + ) { + return; + } + + // TODO ?? If one of the fields is marked as guarded, then activate Password-Reprompt for the entire item + if (field.guarded && cipher.reprompt === CipherRepromptType.None) { + cipher.reprompt = CipherRepromptType.Password; + } + + const fieldType = valueKey === "concealed" ? FieldType.Hidden : FieldType.Text; + this.processKvp(cipher, fieldName, fieldValue, fieldType); + }); + } + + private getFieldName(id: string, title: string): string { + if (this.isNullOrWhitespace(title)) { + return id; + } + + // Naive approach of checking if the fields id is usable + if (id.length > 25 && RegExp(/[0-9]{2}[A-Z]{2}/, "i").test(id)) { + return title; + } + return id; + } + + private extractValue(value: Value, valueKey: string): string { + if (valueKey === "date") { + return new Date(value.date * 1000).toUTCString(); + } + + if (valueKey === "monthYear") { + return value.monthYear.toString(); + } + + return (value as any)[valueKey]; + } + + private fillLogin(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + const fieldName = this.getFieldName(field.id, field.title); + + if (this.isNullOrWhitespace(cipher.login.username) && fieldName === "username") { + cipher.login.username = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.login.password) && fieldName === "password") { + cipher.login.password = fieldValue; + return true; + } + + if ( + this.isNullOrWhitespace(cipher.login.totp) && + field.id != null && + field.id.startsWith("TOTP_") + ) { + cipher.login.totp = fieldValue; + return true; + } + + return false; + } + + private fillApiCredentials(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + const fieldName = this.getFieldName(field.id, field.title); + + if (this.isNullOrWhitespace(cipher.login.password) && fieldName === "credential") { + cipher.login.password = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.login.uri) && fieldName === "hostname") { + cipher.login.uris = this.makeUriArray(fieldValue); + return true; + } + + return false; + } + + private fillCreditCard(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.card.number) && field.id === "ccnum") { + cipher.card.number = fieldValue; + cipher.card.brand = this.getCardBrand(fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.card.code) && field.id === "cvv") { + cipher.card.code = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.card.cardholderName) && field.id === "cardholder") { + cipher.card.cardholderName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.card.expiration) && field.id === "expiry") { + const monthYear: string = fieldValue.toString().trim(); + cipher.card.expMonth = monthYear.substring(4, 6); + if (cipher.card.expMonth[0] === "0") { + cipher.card.expMonth = cipher.card.expMonth.substring(1, 2); + } + cipher.card.expYear = monthYear.substring(0, 4); + return true; + } + + if (field.id === "type") { + // Skip since brand was determined from number above + return true; + } + + return false; + } + + private fillBankAccount(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.card.cardholderName) && field.id === "owner") { + cipher.card.cardholderName = fieldValue; + return true; + } + + return false; + } + + private fillIdentity( + field: FieldsEntity, + fieldValue: string, + cipher: CipherView, + valueKey: string + ): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "firstname") { + cipher.identity.firstName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.lastName) && field.id === "lastname") { + cipher.identity.lastName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.middleName) && field.id === "initial") { + cipher.identity.middleName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.phone) && field.id === "defphone") { + cipher.identity.phone = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "company") { + cipher.identity.company = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.email)) { + if (valueKey === "email") { + const { email_address, provider } = field.value.email; + cipher.identity.email = this.getValueOrDefault(email_address); + this.processKvp(cipher, "provider", provider, FieldType.Text); + return true; + } + + if (field.id === "email") { + cipher.identity.email = fieldValue; + return true; + } + } + + if (this.isNullOrWhitespace(cipher.identity.username) && field.id === "username") { + cipher.identity.username = fieldValue; + return true; + } + return false; + } + + private fillDriversLicense(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "fullname") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.address1) && field.id === "address") { + cipher.identity.address1 = fieldValue; + return true; + } + + // TODO ISO code + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.state) && field.id === "state") { + cipher.identity.state = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.licenseNumber) && field.id === "number") { + cipher.identity.licenseNumber = fieldValue; + return true; + } + + return false; + } + + private fillOutdoorLicense(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "name") { + this.processFullName(cipher, fieldValue); + return true; + } + + // TODO ISO code + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.state) && field.id === "state") { + cipher.identity.state = fieldValue; + return true; + } + + return false; + } + + private fillMembership(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "member_name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "org_name") { + cipher.identity.company = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.phone) && field.id === "phone") { + cipher.identity.phone = fieldValue; + return true; + } + + return false; + } + + private fillPassport(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "fullname") { + this.processFullName(cipher, fieldValue); + return true; + } + + // TODO Iso + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "issuing_country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.passportNumber) && field.id === "number") { + cipher.identity.passportNumber = fieldValue; + return true; + } + + return false; + } + + private fillRewardsProgram(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "member_name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "company_name") { + cipher.identity.company = fieldValue; + return true; + } + + return false; + } + + private fillSSN(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.ssn) && field.id === "number") { + cipher.identity.ssn = fieldValue; + return true; + } + + return false; + } + + private parsePasswordHistory(historyItems: PasswordHistoryEntity[], cipher: CipherView) { + if (historyItems == null || historyItems.length === 0) { + return; + } + + const maxSize = historyItems.length > 5 ? 5 : historyItems.length; + cipher.passwordHistory = historyItems + .filter((h: any) => !this.isNullOrWhitespace(h.value) && h.time != null) + .sort((a, b) => b.time - a.time) + .slice(0, maxSize) + .map((h: any) => { + const ph = new PasswordHistoryView(); + ph.password = h.value; + ph.lastUsedDate = new Date(("" + h.time).length >= 13 ? h.time : h.time * 1000); + return ph; + }); + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepasswordCsvImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepasswordCsvImporter.ts new file mode 100644 index 00000000..d28bdccc --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepasswordCsvImporter.ts @@ -0,0 +1,383 @@ +import { CipherType } from "../../enums/cipherType"; +import { FieldType } from "../../enums/fieldType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CipherView } from "../../models/view/cipherView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { CipherImportContext } from "./cipherImportContext"; + +export const IgnoredProperties = [ + "ainfo", + "autosubmit", + "notesplain", + "ps", + "scope", + "tags", + "title", + "uuid", + "notes", +]; + +export abstract class OnePasswordCsvImporter extends BaseImporter implements Importer { + protected loginPropertyParsers = [ + this.setLoginUsername, + this.setLoginPassword, + this.setLoginUris, + ]; + protected creditCardPropertyParsers = [ + this.setCreditCardNumber, + this.setCreditCardVerification, + this.setCreditCardCardholderName, + this.setCreditCardExpiry, + ]; + protected identityPropertyParsers = [ + this.setIdentityFirstName, + this.setIdentityInitial, + this.setIdentityLastName, + this.setIdentityUserName, + this.setIdentityEmail, + this.setIdentityPhone, + this.setIdentityCompany, + ]; + + abstract setCipherType(value: any, cipher: CipherView): void; + + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true, { + quoteChar: '"', + escapeChar: "\\", + }); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (this.isNullOrWhitespace(this.getProp(value, "title"))) { + return; + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(this.getProp(value, "title"), "--"); + + this.setNotes(value, cipher); + + this.setCipherType(value, cipher); + + let altUsername: string = null; + for (const property in value) { + // eslint-disable-next-line + if (!value.hasOwnProperty(property) || this.isNullOrWhitespace(value[property])) { + continue; + } + + const context = new CipherImportContext(value, property, cipher); + if (cipher.type === CipherType.Login && this.setKnownLoginValue(context)) { + continue; + } else if (cipher.type === CipherType.Card && this.setKnownCreditCardValue(context)) { + continue; + } else if (cipher.type === CipherType.Identity && this.setKnownIdentityValue(context)) { + continue; + } + + altUsername = this.setUnknownValue(context, altUsername); + } + + if ( + cipher.type === CipherType.Login && + !this.isNullOrWhitespace(altUsername) && + this.isNullOrWhitespace(cipher.login.username) && + altUsername.indexOf("://") === -1 + ) { + cipher.login.username = altUsername; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + protected getProp(obj: any, name: string): any { + const lowerObj = Object.entries(obj).reduce((agg: any, entry: [string, any]) => { + agg[entry[0].toLowerCase()] = entry[1]; + return agg; + }, {}); + return lowerObj[name.toLowerCase()]; + } + + protected getPropByRegexp(obj: any, regexp: RegExp): any { + const matchingKeys = Object.keys(obj).reduce((agg: string[], key: string) => { + if (key.match(regexp)) { + agg.push(key); + } + return agg; + }, []); + if (matchingKeys.length === 0) { + return null; + } else { + return obj[matchingKeys[0]]; + } + } + + protected getPropIncluding(obj: any, name: string): any { + const includesMap = Object.keys(obj).reduce((agg: string[], entry: string) => { + if (entry.toLowerCase().includes(name.toLowerCase())) { + agg.push(entry); + } + return agg; + }, []); + if (includesMap.length === 0) { + return null; + } else { + return obj[includesMap[0]]; + } + } + + protected setNotes(importRecord: any, cipher: CipherView) { + cipher.notes = + this.getValueOrDefault(this.getProp(importRecord, "notesPlain"), "") + + "\n" + + this.getValueOrDefault(this.getProp(importRecord, "notes"), "") + + "\n"; + cipher.notes.trim(); + } + + protected setKnownLoginValue(context: CipherImportContext): boolean { + return this.loginPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setKnownCreditCardValue(context: CipherImportContext): boolean { + return this.creditCardPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setKnownIdentityValue(context: CipherImportContext): boolean { + return this.identityPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setUnknownValue(context: CipherImportContext, altUsername: string): string { + if ( + IgnoredProperties.indexOf(context.lowerProperty) === -1 && + !context.lowerProperty.startsWith("section:") && + !context.lowerProperty.startsWith("section ") + ) { + if (altUsername == null && context.lowerProperty === "email") { + return context.importRecord[context.property]; + } else if ( + context.lowerProperty === "created date" || + context.lowerProperty === "modified date" + ) { + const readableDate = new Date( + parseInt(context.importRecord[context.property], 10) * 1000 + ).toUTCString(); + this.processKvp(context.cipher, "1Password " + context.property, readableDate); + return null; + } + if ( + context.lowerProperty.includes("password") || + context.lowerProperty.includes("key") || + context.lowerProperty.includes("secret") + ) { + this.processKvp( + context.cipher, + context.property, + context.importRecord[context.property], + FieldType.Hidden + ); + } else { + this.processKvp(context.cipher, context.property, context.importRecord[context.property]); + } + } + return null; + } + + protected setIdentityFirstName(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.firstName) && + context.lowerProperty.includes("first name") + ) { + context.cipher.identity.firstName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityInitial(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.middleName) && + context.lowerProperty.includes("initial") + ) { + context.cipher.identity.middleName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityLastName(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.lastName) && + context.lowerProperty.includes("last name") + ) { + context.cipher.identity.lastName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityUserName(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.username) && + context.lowerProperty.includes("username") + ) { + context.cipher.identity.username = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityCompany(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.company) && + context.lowerProperty.includes("company") + ) { + context.cipher.identity.company = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityPhone(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.phone) && + context.lowerProperty.includes("default phone") + ) { + context.cipher.identity.phone = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityEmail(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.email) && + context.lowerProperty.includes("email") + ) { + context.cipher.identity.email = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardNumber(context: CipherImportContext): boolean { + if ( + this.isNullOrWhitespace(context.cipher.card.number) && + context.lowerProperty.includes("number") + ) { + context.cipher.card.number = context.importRecord[context.property]; + context.cipher.card.brand = this.getCardBrand(context.cipher.card.number); + return true; + } + return false; + } + + protected setCreditCardVerification(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.card.code) && + context.lowerProperty.includes("verification number") + ) { + context.cipher.card.code = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardCardholderName(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.card.cardholderName) && + context.lowerProperty.includes("cardholder name") + ) { + context.cipher.card.cardholderName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardExpiry(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.card.expiration) && + context.lowerProperty.includes("expiry date") && + context.importRecord[context.property].length === 7 + ) { + context.cipher.card.expMonth = (context.importRecord[context.property] as string).substr( + 0, + 2 + ); + if (context.cipher.card.expMonth[0] === "0") { + context.cipher.card.expMonth = context.cipher.card.expMonth.substr(1, 1); + } + context.cipher.card.expYear = (context.importRecord[context.property] as string).substr(3, 4); + return true; + } + return false; + } + + protected setLoginPassword(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.login.password) && + context.lowerProperty === "password" + ) { + context.cipher.login.password = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setLoginUsername(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.login.username) && + context.lowerProperty === "username" + ) { + context.cipher.login.username = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setLoginUris(context: CipherImportContext) { + if ( + (context.cipher.login.uris == null || context.cipher.login.uris.length === 0) && + context.lowerProperty === "urls" + ) { + const urls = context.importRecord[context.property].split(this.newLineRegex); + context.cipher.login.uris = this.makeUriArray(urls); + return true; + } else if (context.lowerProperty === "url") { + if (context.cipher.login.uris == null) { + context.cipher.login.uris = []; + } + context.cipher.login.uris.concat(this.makeUriArray(context.importRecord[context.property])); + return true; + } + return false; + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts new file mode 100644 index 00000000..2135c80a --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts @@ -0,0 +1,31 @@ +import { CipherType } from "../../enums/cipherType"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { Importer } from "../importer"; + +import { IgnoredProperties, OnePasswordCsvImporter } from "./onepasswordCsvImporter"; + +export class OnePasswordMacCsvImporter extends OnePasswordCsvImporter implements Importer { + setCipherType(value: any, cipher: CipherView) { + const onePassType = this.getValueOrDefault(this.getProp(value, "type"), "Login"); + switch (onePassType) { + case "Credit Card": + cipher.type = CipherType.Card; + cipher.card = new CardView(); + IgnoredProperties.push("type"); + break; + case "Identity": + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + IgnoredProperties.push("type"); + break; + case "Login": + case "Secure Note": + IgnoredProperties.push("type"); + break; + default: + break; + } + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts new file mode 100644 index 00000000..3b3d6454 --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts @@ -0,0 +1,63 @@ +import { CipherType } from "../../enums/cipherType"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { LoginView } from "../../models/view/loginView"; +import { Importer } from "../importer"; + +import { CipherImportContext } from "./cipherImportContext"; +import { OnePasswordCsvImporter } from "./onepasswordCsvImporter"; + +export class OnePasswordWinCsvImporter extends OnePasswordCsvImporter implements Importer { + constructor() { + super(); + this.identityPropertyParsers.push(this.setIdentityAddress); + } + + setCipherType(value: any, cipher: CipherView) { + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + + if ( + !this.isNullOrWhitespace(this.getPropByRegexp(value, /\d+: number/i)) && + !this.isNullOrWhitespace(this.getPropByRegexp(value, /\d+: expiry date/i)) + ) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } + + if ( + !this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: first name/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: initial/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: last name/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /internet \d+: email/i)) + ) { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + } + } + + setIdentityAddress(context: CipherImportContext) { + if (context.lowerProperty.match(/address \d+: address/i)) { + this.processKvp(context.cipher, "address", context.importRecord[context.property]); + return true; + } + return false; + } + + setCreditCardExpiry(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.card.expiration) && + context.lowerProperty.includes("expiry date") + ) { + const expSplit = (context.importRecord[context.property] as string).split("/"); + context.cipher.card.expMonth = expSplit[0]; + if (context.cipher.card.expMonth[0] === "0" && context.cipher.card.expMonth.length === 2) { + context.cipher.card.expMonth = context.cipher.card.expMonth.substr(1, 1); + } + context.cipher.card.expYear = expSplit[2].length > 4 ? expSplit[2].substr(0, 4) : expSplit[2]; + return true; + } + return false; + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts b/jslib/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts new file mode 100644 index 00000000..46ee0619 --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts @@ -0,0 +1,160 @@ +export interface ExportData { + accounts?: AccountsEntity[] | null; +} +export interface AccountsEntity { + attrs: AccountAttributes; + vaults?: VaultsEntity[] | null; +} +export interface AccountAttributes { + accountName: string; + name: string; + avatar: string; + email: string; + uuid: string; + domain: string; +} +export interface VaultsEntity { + attrs: VaultAttributes; + items?: Item[] | null; +} +export interface VaultAttributes { + uuid: string; + desc: string; + avatar: string; + name: string; + type: string; +} + +export enum CategoryEnum { + Login = "001", + CreditCard = "002", + SecureNote = "003", + Identity = "004", + Password = "005", + Document = "006", + SoftwareLicense = "100", + BankAccount = "101", + Database = "102", + DriversLicense = "103", + OutdoorLicense = "104", + Membership = "105", + Passport = "106", + RewardsProgram = "107", + SocialSecurityNumber = "108", + WirelessRouter = "109", + Server = "110", + EmailAccount = "111", + API_Credential = "112", + MedicalRecord = "113", +} + +export interface Item { + uuid: string; + favIndex: number; + createdAt: number; + updatedAt: number; + trashed?: boolean; + categoryUuid: string; + details: Details; + overview: Overview; +} +export interface Details { + loginFields?: (LoginFieldsEntity | null)[] | null; + notesPlain?: string | null; + sections?: (SectionsEntity | null)[] | null; + passwordHistory?: (PasswordHistoryEntity | null)[] | null; + documentAttributes?: DocumentAttributes | null; + password?: string | null; +} + +export enum LoginFieldTypeEnum { + TextOrHtml = "T", + EmailAddress = "E", + URL = "U", + Number = "N", + Password = "P", + TextArea = "A", + PhoneNumber = "T", + CheckBox = "C", +} +export interface LoginFieldsEntity { + value: string; + id: string; + name: string; + fieldType: LoginFieldTypeEnum | string; + designation?: string | null; +} +export interface SectionsEntity { + title: string; + name?: string | null; + fields?: FieldsEntity[] | null; +} +export interface FieldsEntity { + title: string; + id: string; + value: Value; + indexAtSource: number; + guarded: boolean; + multiline: boolean; + dontGenerate: boolean; + placeholder?: string; + inputTraits: InputTraits; + clipboardFilter?: string | null; +} +export interface Value { + totp?: string | null; + date?: number | null; + string?: string | null; + concealed?: string | null; + email?: Email | null; + phone?: string | null; + menu?: string | null; + gender?: string | null; + monthYear?: number | null; + url?: string | null; + address?: Address | null; + creditCardType?: string | null; + creditCardNumber?: string | null; + reference?: string | null; +} + +export interface Email { + email_address: string; + provider: string; +} + +export interface Address { + street: string; + city: string; + country: string; + zip: string; + state: string; +} +export interface InputTraits { + keyboard: string; + correction: string; + capitalization: string; +} +export interface PasswordHistoryEntity { + value: string; + time: number; +} +export interface DocumentAttributes { + fileName: string; + documentId: string; + decryptedSize: number; +} +export interface Overview { + subtitle: string; + title: string; + url: string; + urls?: UrlsEntity[] | null; + ps?: number | null; + pbe?: number | null; + pgrng?: boolean | null; + tags?: string[] | null; +} +export interface UrlsEntity { + label: string; + url: string; +} diff --git a/jslib/common/src/importers/padlockCsvImporter.ts b/jslib/common/src/importers/padlockCsvImporter.ts new file mode 100644 index 00000000..821391cf --- /dev/null +++ b/jslib/common/src/importers/padlockCsvImporter.ts @@ -0,0 +1,85 @@ +import { ImportResult } from "../models/domain/importResult"; +import { CollectionView } from "../models/view/collectionView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PadlockCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + let headers: string[] = null; + results.forEach((value) => { + if (headers == null) { + headers = value.map((v: string) => v); + return; + } + + if (value.length < 2 || value.length !== headers.length) { + return; + } + + if (!this.isNullOrWhitespace(value[1])) { + if (this.organization) { + const tags = (value[1] as string).split(","); + tags.forEach((tag) => { + tag = tag.trim(); + let addCollection = true; + let collectionIndex = result.collections.length; + + for (let i = 0; i < result.collections.length; i++) { + if (result.collections[i].name === tag) { + addCollection = false; + collectionIndex = i; + break; + } + } + + if (addCollection) { + const collection = new CollectionView(); + collection.name = tag; + result.collections.push(collection); + } + + result.collectionRelationships.push([result.ciphers.length, collectionIndex]); + }); + } else { + const tags = (value[1] as string).split(","); + const tag = tags.length > 0 ? tags[0].trim() : null; + this.processFolder(result, tag); + } + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[0], "--"); + + for (let i = 2; i < value.length; i++) { + const header = headers[i].trim().toLowerCase(); + if (this.isNullOrWhitespace(value[i]) || this.isNullOrWhitespace(header)) { + continue; + } + + if (this.usernameFieldNames.indexOf(header) > -1) { + cipher.login.username = value[i]; + } else if (this.passwordFieldNames.indexOf(header) > -1) { + cipher.login.password = value[i]; + } else if (this.uriFieldNames.indexOf(header) > -1) { + cipher.login.uris = this.makeUriArray(value[i]); + } else { + this.processKvp(cipher, headers[i], value[i]); + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passkeepCsvImporter.ts b/jslib/common/src/importers/passkeepCsvImporter.ts new file mode 100644 index 00000000..c93b3a42 --- /dev/null +++ b/jslib/common/src/importers/passkeepCsvImporter.ts @@ -0,0 +1,39 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PassKeepCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + this.processFolder(result, this.getValue("category", value)); + const cipher = this.initLoginCipher(); + cipher.notes = this.getValue("description", value); + cipher.name = this.getValueOrDefault(this.getValue("title", value), "--"); + cipher.login.username = this.getValue("username", value); + cipher.login.password = this.getValue("password", value); + cipher.login.uris = this.makeUriArray(this.getValue("site", value)); + this.processKvp(cipher, "Password 2", this.getValue("password2", value)); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private getValue(key: string, value: any) { + return this.getValueOrDefault(value[key], this.getValueOrDefault(value[" " + key])); + } +} diff --git a/jslib/common/src/importers/passmanJsonImporter.ts b/jslib/common/src/importers/passmanJsonImporter.ts new file mode 100644 index 00000000..f8bc7106 --- /dev/null +++ b/jslib/common/src/importers/passmanJsonImporter.ts @@ -0,0 +1,61 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PassmanJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.length === 0) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((credential: any) => { + if (credential.tags != null && credential.tags.length > 0) { + const folderName = credential.tags[0].text; + this.processFolder(result, folderName); + } + + const cipher = this.initLoginCipher(); + cipher.name = credential.label; + + cipher.login.username = this.getValueOrDefault(credential.username); + if (this.isNullOrWhitespace(cipher.login.username)) { + cipher.login.username = this.getValueOrDefault(credential.email); + } else if (!this.isNullOrWhitespace(credential.email)) { + cipher.notes = "Email: " + credential.email + "\n"; + } + + cipher.login.password = this.getValueOrDefault(credential.password); + cipher.login.uris = this.makeUriArray(credential.url); + cipher.notes += this.getValueOrDefault(credential.description, ""); + if (credential.otp != null) { + cipher.login.totp = this.getValueOrDefault(credential.otp.secret); + } + + if (credential.custom_fields != null) { + credential.custom_fields.forEach((customField: any) => { + switch (customField.field_type) { + case "text": + case "password": + this.processKvp(cipher, customField.label, customField.value); + break; + } + }); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passpackCsvImporter.ts b/jslib/common/src/importers/passpackCsvImporter.ts new file mode 100644 index 00000000..8efaa3c3 --- /dev/null +++ b/jslib/common/src/importers/passpackCsvImporter.ts @@ -0,0 +1,103 @@ +import { ImportResult } from "../models/domain/importResult"; +import { CollectionView } from "../models/view/collectionView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasspackCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const tagsJson = !this.isNullOrWhitespace(value.Tags) ? JSON.parse(value.Tags) : null; + const tags: string[] = + tagsJson != null && tagsJson.tags != null && tagsJson.tags.length > 0 + ? tagsJson.tags + .map((tagJson: string) => { + try { + const t = JSON.parse(tagJson); + return this.getValueOrDefault(t.tag); + } catch { + // Ignore error + } + return null; + }) + .filter((t: string) => !this.isNullOrWhitespace(t)) + : null; + + if (this.organization && tags != null && tags.length > 0) { + tags.forEach((tag) => { + let addCollection = true; + let collectionIndex = result.collections.length; + + for (let i = 0; i < result.collections.length; i++) { + if (result.collections[i].name === tag) { + addCollection = false; + collectionIndex = i; + break; + } + } + + if (addCollection) { + const collection = new CollectionView(); + collection.name = tag; + result.collections.push(collection); + } + + result.collectionRelationships.push([result.ciphers.length, collectionIndex]); + }); + } else if (!this.organization && tags != null && tags.length > 0) { + this.processFolder(result, tags[0]); + } + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value.Notes, ""); + cipher.notes += "\n\n" + this.getValueOrDefault(value["Shared Notes"], "") + "\n"; + cipher.name = this.getValueOrDefault(value["Entry Name"], "--"); + cipher.login.username = this.getValueOrDefault(value["User ID"]); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.URL); + + if (value.__parsed_extra != null && value.__parsed_extra.length > 0) { + value.__parsed_extra.forEach((extra: string) => { + if (!this.isNullOrWhitespace(extra)) { + cipher.notes += "\n" + extra; + } + }); + } + + const fieldsJson = !this.isNullOrWhitespace(value["Extra Fields"]) + ? JSON.parse(value["Extra Fields"]) + : null; + const fields = + fieldsJson != null && fieldsJson.extraFields != null && fieldsJson.extraFields.length > 0 + ? fieldsJson.extraFields.map((fieldJson: string) => { + try { + return JSON.parse(fieldJson); + } catch { + // Ignore error + } + return null; + }) + : null; + if (fields != null) { + fields.forEach((f: any) => { + if (f != null) { + this.processKvp(cipher, f.name, f.data); + } + }); + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordAgentCsvImporter.ts b/jslib/common/src/importers/passwordAgentCsvImporter.ts new file mode 100644 index 00000000..cc37f5dd --- /dev/null +++ b/jslib/common/src/importers/passwordAgentCsvImporter.ts @@ -0,0 +1,52 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordAgentCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + let newVersion = true; + results.forEach((value) => { + if (value.length !== 5 && value.length < 9) { + return; + } + const altFormat = value.length === 10 && value[0] === "0"; + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[altFormat ? 1 : 0], "--"); + cipher.login.username = this.getValueOrDefault(value[altFormat ? 2 : 1]); + cipher.login.password = this.getValueOrDefault(value[altFormat ? 3 : 2]); + if (value.length === 5) { + newVersion = false; + cipher.notes = this.getValueOrDefault(value[4]); + cipher.login.uris = this.makeUriArray(value[3]); + } else { + const folder = this.getValueOrDefault(value[altFormat ? 9 : 8], "(None)"); + let folderName = folder !== "(None)" ? folder.split("\\").join("/") : null; + if (folderName != null) { + folderName = folder.split(" > ").join("/"); + folderName = folder.split(">").join("/"); + } + this.processFolder(result, folderName); + cipher.notes = this.getValueOrDefault(value[altFormat ? 5 : 3]); + cipher.login.uris = this.makeUriArray(value[4]); + } + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (newVersion && this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordBossJsonImporter.ts b/jslib/common/src/importers/passwordBossJsonImporter.ts new file mode 100644 index 00000000..d60a84fb --- /dev/null +++ b/jslib/common/src/importers/passwordBossJsonImporter.ts @@ -0,0 +1,130 @@ +import { CipherType } from "../enums/cipherType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { FolderView } from "../models/view/folderView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordBossJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.items == null) { + result.success = false; + return Promise.resolve(result); + } + + const foldersMap = new Map(); + results.folders.forEach((value: any) => { + foldersMap.set(value.id, value.name); + }); + const foldersIndexMap = new Map(); + foldersMap.forEach((val, key) => { + foldersIndexMap.set(key, result.folders.length); + const f = new FolderView(); + f.name = val; + result.folders.push(f); + }); + + results.items.forEach((value: any) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.name, "--"); + cipher.login.uris = this.makeUriArray(value.login_url); + + if (value.folder != null && foldersIndexMap.has(value.folder)) { + result.folderRelationships.push([result.ciphers.length, foldersIndexMap.get(value.folder)]); + } + + if (value.identifiers == null) { + return; + } + + if (!this.isNullOrWhitespace(value.identifiers.notes)) { + cipher.notes = value.identifiers.notes.split("\\r\\n").join("\n").split("\\n").join("\n"); + } + + if (value.type === "CreditCard") { + cipher.card = new CardView(); + cipher.type = CipherType.Card; + } + + for (const property in value.identifiers) { + // eslint-disable-next-line + if (!value.identifiers.hasOwnProperty(property)) { + continue; + } + const valObj = value.identifiers[property]; + const val = valObj != null ? valObj.toString() : null; + if ( + this.isNullOrWhitespace(val) || + property === "notes" || + property === "ignoreItemInSecurityScore" + ) { + continue; + } + + if (property === "custom_fields") { + valObj.forEach((cf: any) => { + this.processKvp(cipher, cf.name, cf.value); + }); + continue; + } + + if (cipher.type === CipherType.Card) { + if (property === "cardNumber") { + cipher.card.number = val; + cipher.card.brand = this.getCardBrand(val); + continue; + } else if (property === "nameOnCard") { + cipher.card.cardholderName = val; + continue; + } else if (property === "security_code") { + cipher.card.code = val; + continue; + } else if (property === "expires") { + try { + const expDate = new Date(val); + cipher.card.expYear = expDate.getFullYear().toString(); + cipher.card.expMonth = (expDate.getMonth() + 1).toString(); + } catch { + // Ignore error + } + continue; + } else if (property === "cardType") { + continue; + } + } else { + if ( + (property === "username" || property === "email") && + this.isNullOrWhitespace(cipher.login.username) + ) { + cipher.login.username = val; + continue; + } else if (property === "password") { + cipher.login.password = val; + continue; + } else if (property === "totp") { + cipher.login.totp = val; + continue; + } else if ( + (cipher.login.uris == null || cipher.login.uris.length === 0) && + this.uriFieldNames.indexOf(property) > -1 + ) { + cipher.login.uris = this.makeUriArray(val); + continue; + } + } + + this.processKvp(cipher, property, val); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordDragonXmlImporter.ts b/jslib/common/src/importers/passwordDragonXmlImporter.ts new file mode 100644 index 00000000..433950ad --- /dev/null +++ b/jslib/common/src/importers/passwordDragonXmlImporter.ts @@ -0,0 +1,63 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordDragonXmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const records = doc.querySelectorAll("PasswordManager > record"); + Array.from(records).forEach((record) => { + const category = this.querySelectorDirectChild(record, "Category"); + const categoryText = + category != null && + !this.isNullOrWhitespace(category.textContent) && + category.textContent !== "Unfiled" + ? category.textContent + : null; + this.processFolder(result, categoryText); + + const accountName = this.querySelectorDirectChild(record, "Account-Name"); + const userId = this.querySelectorDirectChild(record, "User-Id"); + const password = this.querySelectorDirectChild(record, "Password"); + const url = this.querySelectorDirectChild(record, "URL"); + const notes = this.querySelectorDirectChild(record, "Notes"); + const cipher = this.initLoginCipher(); + cipher.name = + accountName != null ? this.getValueOrDefault(accountName.textContent, "--") : "--"; + cipher.notes = notes != null ? this.getValueOrDefault(notes.textContent) : ""; + cipher.login.username = userId != null ? this.getValueOrDefault(userId.textContent) : null; + cipher.login.password = + password != null ? this.getValueOrDefault(password.textContent) : null; + cipher.login.uris = url != null ? this.makeUriArray(url.textContent) : null; + + const attributes: string[] = []; + for (let i = 1; i <= 10; i++) { + attributes.push("Attribute-" + i); + } + + this.querySelectorAllDirectChild(record, attributes.join(",")).forEach((attr) => { + if (this.isNullOrWhitespace(attr.textContent) || attr.textContent === "null") { + return; + } + this.processKvp(cipher, attr.tagName, attr.textContent); + }); + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordSafeXmlImporter.ts b/jslib/common/src/importers/passwordSafeXmlImporter.ts new file mode 100644 index 00000000..b7aa9358 --- /dev/null +++ b/jslib/common/src/importers/passwordSafeXmlImporter.ts @@ -0,0 +1,69 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordSafeXmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const passwordSafe = doc.querySelector("passwordsafe"); + if (passwordSafe == null) { + result.errorMessage = "Missing `passwordsafe` node."; + result.success = false; + return Promise.resolve(result); + } + + const notesDelimiter = passwordSafe.getAttribute("delimiter"); + const entries = doc.querySelectorAll("passwordsafe > entry"); + Array.from(entries).forEach((entry) => { + const group = this.querySelectorDirectChild(entry, "group"); + const groupText = + group != null && !this.isNullOrWhitespace(group.textContent) + ? group.textContent.split(".").join("/") + : null; + this.processFolder(result, groupText); + + const title = this.querySelectorDirectChild(entry, "title"); + const username = this.querySelectorDirectChild(entry, "username"); + const email = this.querySelectorDirectChild(entry, "email"); + const password = this.querySelectorDirectChild(entry, "password"); + const url = this.querySelectorDirectChild(entry, "url"); + const notes = this.querySelectorDirectChild(entry, "notes"); + const cipher = this.initLoginCipher(); + cipher.name = title != null ? this.getValueOrDefault(title.textContent, "--") : "--"; + cipher.notes = + notes != null + ? this.getValueOrDefault(notes.textContent, "").split(notesDelimiter).join("\n") + : null; + cipher.login.username = + username != null ? this.getValueOrDefault(username.textContent) : null; + cipher.login.password = + password != null ? this.getValueOrDefault(password.textContent) : null; + cipher.login.uris = url != null ? this.makeUriArray(url.textContent) : null; + + if (this.isNullOrWhitespace(cipher.login.username) && email != null) { + cipher.login.username = this.getValueOrDefault(email.textContent); + } else if (email != null && !this.isNullOrWhitespace(email.textContent)) { + cipher.notes = this.isNullOrWhitespace(cipher.notes) + ? "Email: " + email.textContent + : cipher.notes + "\n" + "Email: " + email.textContent; + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordWalletTxtImporter.ts b/jslib/common/src/importers/passwordWalletTxtImporter.ts new file mode 100644 index 00000000..b5940372 --- /dev/null +++ b/jslib/common/src/importers/passwordWalletTxtImporter.ts @@ -0,0 +1,47 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordWalletTxtImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 1) { + return; + } + if (value.length > 5) { + this.processFolder(result, value[5]); + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[0], "--"); + if (value.length > 4) { + cipher.notes = this.getValueOrDefault(value[4], "").split("¬").join("\n"); + } + if (value.length > 2) { + cipher.login.username = this.getValueOrDefault(value[2]); + } + if (value.length > 3) { + cipher.login.password = this.getValueOrDefault(value[3]); + } + if (value.length > 1) { + cipher.login.uris = this.makeUriArray(value[1]); + } + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/rememBearCsvImporter.ts b/jslib/common/src/importers/rememBearCsvImporter.ts new file mode 100644 index 00000000..3f7dc464 --- /dev/null +++ b/jslib/common/src/importers/rememBearCsvImporter.ts @@ -0,0 +1,75 @@ +import { CipherType } from "../enums/cipherType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class RememBearCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.trash === "true") { + return; + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.name); + cipher.notes = this.getValueOrDefault(value.notes); + if (value.type === "LoginItem") { + cipher.login.uris = this.makeUriArray(value.website); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.username = this.getValueOrDefault(value.username); + } else if (value.type === "CreditCardItem") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(value.cardholder); + cipher.card.number = this.getValueOrDefault(value.number); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = this.getValueOrDefault(value.verification); + + try { + const expMonth = this.getValueOrDefault(value.expiryMonth); + if (expMonth != null) { + const expMonthNumber = parseInt(expMonth, null); + if (expMonthNumber != null && expMonthNumber >= 1 && expMonthNumber <= 12) { + cipher.card.expMonth = expMonthNumber.toString(); + } + } + } catch { + // Ignore error + } + try { + const expYear = this.getValueOrDefault(value.expiryYear); + if (expYear != null) { + const expYearNumber = parseInt(expYear, null); + if (expYearNumber != null) { + cipher.card.expYear = expYearNumber.toString(); + } + } + } catch { + // Ignore error + } + + const pin = this.getValueOrDefault(value.pin); + if (pin != null) { + this.processKvp(cipher, "PIN", pin); + } + const zip = this.getValueOrDefault(value.zipCode); + if (zip != null) { + this.processKvp(cipher, "Zip Code", zip); + } + } + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/roboformCsvImporter.ts b/jslib/common/src/importers/roboformCsvImporter.ts new file mode 100644 index 00000000..c5279e2b --- /dev/null +++ b/jslib/common/src/importers/roboformCsvImporter.ts @@ -0,0 +1,69 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class RoboFormCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + let i = 1; + results.forEach((value) => { + const folder = + !this.isNullOrWhitespace(value.Folder) && value.Folder.startsWith("/") + ? value.Folder.replace("/", "") + : value.Folder; + const folderName = !this.isNullOrWhitespace(folder) ? folder : null; + this.processFolder(result, folderName); + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value.Note); + cipher.name = this.getValueOrDefault(value.Name, "--"); + cipher.login.username = this.getValueOrDefault(value.Login); + cipher.login.password = this.getValueOrDefault(value.Pwd); + cipher.login.uris = this.makeUriArray(value.Url); + + if (!this.isNullOrWhitespace(value.Rf_fields)) { + let fields: string[] = [value.Rf_fields]; + if (value.__parsed_extra != null && value.__parsed_extra.length > 0) { + fields = fields.concat(value.__parsed_extra); + } + fields.forEach((field: string) => { + const parts = field.split(":"); + if (parts.length < 3) { + return; + } + const key = parts[0] === "-no-name-" ? null : parts[0]; + const val = parts.length === 4 && parts[2] === "rck" ? parts[1] : parts[2]; + this.processKvp(cipher, key, val); + }); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + + if ( + i === results.length && + cipher.name === "--" && + this.isNullOrWhitespace(cipher.login.password) + ) { + return; + } + + result.ciphers.push(cipher); + i++; + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/safariCsvImporter.ts b/jslib/common/src/importers/safariCsvImporter.ts new file mode 100644 index 00000000..786b2827 --- /dev/null +++ b/jslib/common/src/importers/safariCsvImporter.ts @@ -0,0 +1,30 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SafariCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.Title, "--"); + cipher.login.username = this.getValueOrDefault(value.Username); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.Url ?? value.URL); + cipher.login.totp = this.getValueOrDefault(value.OTPAuth); + cipher.notes = this.getValueOrDefault(value.Notes); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/safeInCloudXmlImporter.ts b/jslib/common/src/importers/safeInCloudXmlImporter.ts new file mode 100644 index 00000000..3e5a8199 --- /dev/null +++ b/jslib/common/src/importers/safeInCloudXmlImporter.ts @@ -0,0 +1,132 @@ +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; +import { FieldView } from "../models/view/fieldView"; +import { FolderView } from "../models/view/folderView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SafeInCloudXmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const db = doc.querySelector("database"); + if (db == null) { + result.errorMessage = "Missing `database` node."; + result.success = false; + return Promise.resolve(result); + } + + const foldersMap = new Map(); + + Array.from(doc.querySelectorAll("database > label")).forEach((labelEl) => { + const name = labelEl.getAttribute("name"); + const id = labelEl.getAttribute("id"); + if (!this.isNullOrWhitespace(name) && !this.isNullOrWhitespace(id)) { + foldersMap.set(id, result.folders.length); + const folder = new FolderView(); + folder.name = name; + result.folders.push(folder); + } + }); + + Array.from(doc.querySelectorAll("database > card")).forEach((cardEl) => { + if (cardEl.getAttribute("template") === "true" || cardEl.getAttribute("deleted") === "true") { + return; + } + + const labelIdEl = this.querySelectorDirectChild(cardEl, "label_id"); + if (labelIdEl != null) { + const labelId = labelIdEl.textContent; + if (!this.isNullOrWhitespace(labelId) && foldersMap.has(labelId)) { + result.folderRelationships.push([result.ciphers.length, foldersMap.get(labelId)]); + } + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(cardEl.getAttribute("title"), "--"); + + if (cardEl.getAttribute("star") === "true") { + cipher.favorite = true; + } + + const cardType = cardEl.getAttribute("type"); + if (cardType === "note") { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + } else { + Array.from(this.querySelectorAllDirectChild(cardEl, "field")).forEach((fieldEl) => { + const text = fieldEl.textContent; + if (this.isNullOrWhitespace(text)) { + return; + } + const name = fieldEl.getAttribute("name"); + const fieldType = this.getValueOrDefault(fieldEl.getAttribute("type"), "").toLowerCase(); + if (fieldType === "login") { + cipher.login.username = text; + } else if (fieldType === "password" || fieldType === "secret") { + // safeInCloud allows for more than one password. we just insert them here and find the one used as password later + this.processKvp(cipher, name, text, FieldType.Hidden); + } else if (fieldType === "one_time_password") { + cipher.login.totp = text; + } else if (fieldType === "notes") { + cipher.notes += text + "\n"; + } else if (fieldType === "weblogin" || fieldType === "website") { + cipher.login.uris = this.makeUriArray(text); + } else { + this.processKvp(cipher, name, text); + } + }); + } + + Array.from(this.querySelectorAllDirectChild(cardEl, "notes")).forEach((notesEl) => { + cipher.notes += notesEl.textContent + "\n"; + }); + + this.setPassword(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + // Choose a password from all passwords. Take one that has password in its name, or the first one if there is no such entry + // if its name is password, we can safely remove it form the fields. otherwise, it would maybe be best to keep it as a hidden field + setPassword(cipher: CipherView) { + const candidates = cipher.fields.filter((field) => field.type === FieldType.Hidden); + if (!candidates.length) { + return; + } + + let choice: FieldView; + for (const field of candidates) { + if (this.passwordFieldNames.includes(field.name.toLowerCase())) { + choice = field; + cipher.fields = cipher.fields.filter((f) => f !== choice); + break; + } + } + + if (!choice) { + choice = candidates[0]; + } + + cipher.login.password = choice.value; + } +} diff --git a/jslib/common/src/importers/saferpassCsvImport.ts b/jslib/common/src/importers/saferpassCsvImport.ts new file mode 100644 index 00000000..6d962ba1 --- /dev/null +++ b/jslib/common/src/importers/saferpassCsvImport.ts @@ -0,0 +1,29 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SaferPassCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(this.nameFromUrl(value.url), "--"); + cipher.notes = this.getValueOrDefault(value.notes); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.url); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/secureSafeCsvImporter.ts b/jslib/common/src/importers/secureSafeCsvImporter.ts new file mode 100644 index 00000000..8d0ac1fe --- /dev/null +++ b/jslib/common/src/importers/secureSafeCsvImporter.ts @@ -0,0 +1,29 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SecureSafeCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.Title); + cipher.notes = this.getValueOrDefault(value.Comment); + cipher.login.uris = this.makeUriArray(value.Url); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.username = this.getValueOrDefault(value.Username); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/splashIdCsvImporter.ts b/jslib/common/src/importers/splashIdCsvImporter.ts new file mode 100644 index 00000000..df1118d1 --- /dev/null +++ b/jslib/common/src/importers/splashIdCsvImporter.ts @@ -0,0 +1,57 @@ +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SplashIdCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 3) { + return; + } + + this.processFolder(result, this.getValueOrDefault(value[value.length - 1])); + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value[value.length - 2], ""); + cipher.name = this.getValueOrDefault(value[1], "--"); + + if (value[0] === "Web Logins" || value[0] === "Servers" || value[0] === "Email Accounts") { + cipher.login.username = this.getValueOrDefault(value[2]); + cipher.login.password = this.getValueOrDefault(value[3]); + cipher.login.uris = this.makeUriArray(value[4]); + this.parseFieldsToNotes(cipher, 5, value); + } else { + this.parseFieldsToNotes(cipher, 2, value); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private parseFieldsToNotes(cipher: CipherView, startIndex: number, value: any) { + // last 3 rows do not get parsed + for (let i = startIndex; i < value.length - 3; i++) { + if (this.isNullOrWhitespace(value[i])) { + continue; + } + cipher.notes += value[i] + "\n"; + } + } +} diff --git a/jslib/common/src/importers/stickyPasswordXmlImporter.ts b/jslib/common/src/importers/stickyPasswordXmlImporter.ts new file mode 100644 index 00000000..caaa3370 --- /dev/null +++ b/jslib/common/src/importers/stickyPasswordXmlImporter.ts @@ -0,0 +1,83 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class StickyPasswordXmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const loginNodes = doc.querySelectorAll("root > Database > Logins > Login"); + Array.from(loginNodes).forEach((loginNode) => { + const accountId = loginNode.getAttribute("ID"); + if (this.isNullOrWhitespace(accountId)) { + return; + } + + const usernameText = loginNode.getAttribute("Name"); + const passwordText = loginNode.getAttribute("Password"); + let titleText: string = null; + let linkText: string = null; + let notesText: string = null; + let groupId: string = null; + let groupText: string = null; + + const accountLogin = doc.querySelector( + "root > Database > Accounts > Account > " + + 'LoginLinks > Login[SourceLoginID="' + + accountId + + '"]' + ); + if (accountLogin != null) { + const account = accountLogin.parentElement.parentElement; + if (account != null) { + titleText = account.getAttribute("Name"); + linkText = account.getAttribute("Link"); + groupId = account.getAttribute("ParentID"); + notesText = account.getAttribute("Comments"); + if (!this.isNullOrWhitespace(notesText)) { + notesText = notesText.split("/n").join("\n"); + } + } + } + + if (!this.isNullOrWhitespace(groupId)) { + groupText = this.buildGroupText(doc, groupId, ""); + this.processFolder(result, groupText); + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(titleText, "--"); + cipher.notes = this.getValueOrDefault(notesText); + cipher.login.username = this.getValueOrDefault(usernameText); + cipher.login.password = this.getValueOrDefault(passwordText); + cipher.login.uris = this.makeUriArray(linkText); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + buildGroupText(doc: Document, groupId: string, groupText: string): string { + const group = doc.querySelector('root > Database > Groups > Group[ID="' + groupId + '"]'); + if (group == null) { + return groupText; + } + if (!this.isNullOrWhitespace(groupText)) { + groupText = "/" + groupText; + } + groupText = group.getAttribute("Name") + groupText; + return this.buildGroupText(doc, group.getAttribute("ParentID"), groupText); + } +} diff --git a/jslib/common/src/importers/truekeyCsvImporter.ts b/jslib/common/src/importers/truekeyCsvImporter.ts new file mode 100644 index 00000000..d28af7af --- /dev/null +++ b/jslib/common/src/importers/truekeyCsvImporter.ts @@ -0,0 +1,87 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +const PropertiesToIgnore = [ + "kind", + "autologin", + "favorite", + "hexcolor", + "protectedwithpassword", + "subdomainonly", + "type", + "tk_export_version", + "note", + "title", + "document_content", +]; + +export class TrueKeyCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.favorite = this.getValueOrDefault(value.favorite, "").toLowerCase() === "true"; + cipher.name = this.getValueOrDefault(value.name, "--"); + cipher.notes = this.getValueOrDefault(value.memo, ""); + cipher.login.username = this.getValueOrDefault(value.login); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.url); + + if (value.kind !== "login") { + cipher.name = this.getValueOrDefault(value.title, "--"); + cipher.notes = this.getValueOrDefault(value.note, ""); + } + + if (value.kind === "cc") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(value.cardholder); + cipher.card.number = this.getValueOrDefault(value.number); + cipher.card.brand = this.getCardBrand(cipher.card.number); + if (!this.isNullOrWhitespace(value.expiryDate)) { + try { + const expDate = new Date(value.expiryDate); + cipher.card.expYear = expDate.getFullYear().toString(); + cipher.card.expMonth = (expDate.getMonth() + 1).toString(); + } catch { + // Ignore error + } + } + } else if (value.kind !== "login") { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + if (!this.isNullOrWhitespace(cipher.notes)) { + cipher.notes = this.getValueOrDefault(value.document_content, ""); + } + for (const property in value) { + if ( + value.hasOwnProperty(property) && // eslint-disable-line + PropertiesToIgnore.indexOf(property.toLowerCase()) < 0 && + !this.isNullOrWhitespace(value[property]) + ) { + this.processKvp(cipher, property, value[property]); + } + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/upmCsvImporter.ts b/jslib/common/src/importers/upmCsvImporter.ts new file mode 100644 index 00000000..ea092ffc --- /dev/null +++ b/jslib/common/src/importers/upmCsvImporter.ts @@ -0,0 +1,32 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class UpmCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length !== 5) { + return; + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[0], "--"); + cipher.notes = this.getValueOrDefault(value[4]); + cipher.login.username = this.getValueOrDefault(value[1]); + cipher.login.password = this.getValueOrDefault(value[2]); + cipher.login.uris = this.makeUriArray(value[3]); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/yotiCsvImporter.ts b/jslib/common/src/importers/yotiCsvImporter.ts new file mode 100644 index 00000000..526f7d69 --- /dev/null +++ b/jslib/common/src/importers/yotiCsvImporter.ts @@ -0,0 +1,28 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class YotiCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.Name, "--"); + cipher.login.username = this.getValueOrDefault(value["User name"]); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.URL); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/zohoVaultCsvImporter.ts b/jslib/common/src/importers/zohoVaultCsvImporter.ts new file mode 100644 index 00000000..18008e80 --- /dev/null +++ b/jslib/common/src/importers/zohoVaultCsvImporter.ts @@ -0,0 +1,81 @@ +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class ZohoVaultCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if ( + this.isNullOrWhitespace(value["Password Name"]) && + this.isNullOrWhitespace(value["Secret Name"]) + ) { + return; + } + this.processFolder(result, this.getValueOrDefault(value.ChamberName)); + const cipher = this.initLoginCipher(); + cipher.favorite = this.getValueOrDefault(value.Favorite, "0") === "1"; + cipher.notes = this.getValueOrDefault(value.Notes); + cipher.name = this.getValueOrDefault( + value["Password Name"], + this.getValueOrDefault(value["Secret Name"], "--") + ); + cipher.login.uris = this.makeUriArray( + this.getValueOrDefault(value["Password URL"], this.getValueOrDefault(value["Secret URL"])) + ); + this.parseData(cipher, value.SecretData); + this.parseData(cipher, value.CustomData); + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private parseData(cipher: CipherView, data: string) { + if (this.isNullOrWhitespace(data)) { + return; + } + const dataLines = this.splitNewLine(data); + dataLines.forEach((line) => { + const delimPosition = line.indexOf(":"); + if (delimPosition < 0) { + return; + } + const field = line.substring(0, delimPosition); + const value = line.length > delimPosition ? line.substring(delimPosition + 1) : null; + if ( + this.isNullOrWhitespace(field) || + this.isNullOrWhitespace(value) || + field === "SecretType" + ) { + return; + } + const fieldLower = field.toLowerCase(); + if (cipher.login.username == null && this.usernameFieldNames.indexOf(fieldLower) > -1) { + cipher.login.username = value; + } else if ( + cipher.login.password == null && + this.passwordFieldNames.indexOf(fieldLower) > -1 + ) { + cipher.login.password = value; + } else { + this.processKvp(cipher, field, value); + } + }); + } +} diff --git a/jslib/common/src/misc/captcha_iframe.ts b/jslib/common/src/misc/captcha_iframe.ts new file mode 100644 index 00000000..ca422fc0 --- /dev/null +++ b/jslib/common/src/misc/captcha_iframe.ts @@ -0,0 +1,38 @@ +import { I18nService } from "../abstractions/i18n.service"; + +import { IFrameComponent } from "./iframe_component"; + +export class CaptchaIFrame extends IFrameComponent { + constructor( + win: Window, + webVaultUrl: string, + private i18nService: I18nService, + successCallback: (message: string) => any, + errorCallback: (message: string) => any, + infoCallback: (message: string) => any + ) { + super( + win, + webVaultUrl, + "captcha-connector.html", + "hcaptcha_iframe", + successCallback, + errorCallback, + (message: string) => { + const parsedMessage = JSON.parse(message); + if (typeof parsedMessage !== "string") { + this.iframe.height = parsedMessage.height.toString(); + this.iframe.width = parsedMessage.width.toString(); + } else { + infoCallback(parsedMessage); + } + } + ); + } + + init(siteKey: string): void { + super.initComponent( + this.createParams({ siteKey: siteKey, locale: this.i18nService.translationLocale }, 1) + ); + } +} diff --git a/jslib/common/src/misc/iframe_component.ts b/jslib/common/src/misc/iframe_component.ts new file mode 100644 index 00000000..2cd702ad --- /dev/null +++ b/jslib/common/src/misc/iframe_component.ts @@ -0,0 +1,94 @@ +export abstract class IFrameComponent { + iframe: HTMLIFrameElement; + private connectorLink: HTMLAnchorElement; + private parseFunction = this.parseMessage.bind(this); + + constructor( + private win: Window, + protected webVaultUrl: string, + private path: string, + private iframeId: string, + public successCallback?: (message: string) => any, + public errorCallback?: (message: string) => any, + public infoCallback?: (message: string) => any + ) { + this.connectorLink = win.document.createElement("a"); + } + + stop() { + this.sendMessage("stop"); + } + + start() { + this.sendMessage("start"); + } + + sendMessage(message: any) { + if (!this.iframe || !this.iframe.src || !this.iframe.contentWindow) { + return; + } + + this.iframe.contentWindow.postMessage(message, this.iframe.src); + } + + base64Encode(str: string): string { + return btoa( + encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => { + return String.fromCharCode(("0x" + p1) as any); + }) + ); + } + + cleanup() { + this.win.removeEventListener("message", this.parseFunction, false); + } + + protected createParams(data: any, version: number) { + return new URLSearchParams({ + data: this.base64Encode(JSON.stringify(data)), + parent: encodeURIComponent(this.win.document.location.href), + v: version.toString(), + }); + } + + protected initComponent(params: URLSearchParams): void { + this.connectorLink.href = `${this.webVaultUrl}/${this.path}?${params}`; + this.iframe = this.win.document.getElementById(this.iframeId) as HTMLIFrameElement; + this.iframe.src = this.connectorLink.href; + + this.win.addEventListener("message", this.parseFunction, false); + } + + private parseMessage(event: MessageEvent) { + if (!this.validMessage(event)) { + return; + } + + const parts: string[] = event.data.split("|"); + if (parts[0] === "success" && this.successCallback) { + this.successCallback(parts[1]); + } else if (parts[0] === "error" && this.errorCallback) { + this.errorCallback(parts[1]); + } else if (parts[0] === "info" && this.infoCallback) { + this.infoCallback(parts[1]); + } + } + + private validMessage(event: MessageEvent) { + if ( + event.origin == null || + event.origin === "" || + event.origin !== (this.connectorLink as any).origin || + event.data == null || + typeof event.data !== "string" + ) { + return false; + } + + return ( + event.data.indexOf("success|") === 0 || + event.data.indexOf("error|") === 0 || + event.data.indexOf("info|") === 0 + ); + } +} diff --git a/jslib/common/src/misc/linkedFieldOption.decorator.ts b/jslib/common/src/misc/linkedFieldOption.decorator.ts new file mode 100644 index 00000000..43836318 --- /dev/null +++ b/jslib/common/src/misc/linkedFieldOption.decorator.ts @@ -0,0 +1,27 @@ +import { LinkedIdType } from "../enums/linkedIdType"; +import { ItemView } from "../models/view/itemView"; + +export class LinkedMetadata { + constructor(readonly propertyKey: string, private readonly _i18nKey?: string) {} + + get i18nKey() { + return this._i18nKey ?? this.propertyKey; + } +} + +/** + * A decorator used to set metadata used by Linked custom fields. Apply it to a class property or getter to make it + * available as a Linked custom field option. + * @param id - A unique value that is saved in the Field model. It is used to look up the decorated class property. + * @param i18nKey - The i18n key used to describe the decorated class property in the UI. If it is null, then the name + * of the class property will be used as the i18n key. + */ +export function linkedFieldOption(id: LinkedIdType, i18nKey?: string) { + return (prototype: ItemView, propertyKey: string) => { + if (prototype.linkedFieldOptions == null) { + prototype.linkedFieldOptions = new Map(); + } + + prototype.linkedFieldOptions.set(id, new LinkedMetadata(propertyKey, i18nKey)); + }; +} diff --git a/jslib/common/src/misc/logInStrategies/apiLogin.strategy.ts b/jslib/common/src/misc/logInStrategies/apiLogin.strategy.ts new file mode 100644 index 00000000..89ebc379 --- /dev/null +++ b/jslib/common/src/misc/logInStrategies/apiLogin.strategy.ts @@ -0,0 +1,70 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { EnvironmentService } from "../../abstractions/environment.service"; +import { KeyConnectorService } from "../../abstractions/keyConnector.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { ApiLogInCredentials } from "../../models/domain/logInCredentials"; +import { ApiTokenRequest } from "../../models/request/identityToken/apiTokenRequest"; +import { IdentityTokenResponse } from "../../models/response/identityTokenResponse"; + +import { LogInStrategy } from "./logIn.strategy"; + +export class ApiLogInStrategy extends LogInStrategy { + tokenRequest: ApiTokenRequest; + + constructor( + cryptoService: CryptoService, + apiService: ApiService, + tokenService: TokenService, + appIdService: AppIdService, + platformUtilsService: PlatformUtilsService, + messagingService: MessagingService, + logService: LogService, + stateService: StateService, + twoFactorService: TwoFactorService, + private environmentService: EnvironmentService, + private keyConnectorService: KeyConnectorService + ) { + super( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService + ); + } + + async onSuccessfulLogin(tokenResponse: IdentityTokenResponse) { + if (tokenResponse.apiUseKeyConnector) { + const keyConnectorUrl = this.environmentService.getKeyConnectorUrl(); + await this.keyConnectorService.getAndSetKey(keyConnectorUrl); + } + } + + async logIn(credentials: ApiLogInCredentials) { + this.tokenRequest = new ApiTokenRequest( + credentials.clientId, + credentials.clientSecret, + await this.buildTwoFactor(), + await this.buildDeviceRequest() + ); + + return this.startLogIn(); + } + + protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) { + await super.saveAccountInformation(tokenResponse); + await this.stateService.setApiKeyClientId(this.tokenRequest.clientId); + await this.stateService.setApiKeyClientSecret(this.tokenRequest.clientSecret); + } +} diff --git a/jslib/common/src/misc/logInStrategies/logIn.strategy.ts b/jslib/common/src/misc/logInStrategies/logIn.strategy.ts new file mode 100644 index 00000000..a58a7bc8 --- /dev/null +++ b/jslib/common/src/misc/logInStrategies/logIn.strategy.ts @@ -0,0 +1,170 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; +import { Account, AccountProfile, AccountTokens } from "../../models/domain/account"; +import { AuthResult } from "../../models/domain/authResult"; +import { + ApiLogInCredentials, + PasswordLogInCredentials, + SsoLogInCredentials, +} from "../../models/domain/logInCredentials"; +import { DeviceRequest } from "../../models/request/deviceRequest"; +import { ApiTokenRequest } from "../../models/request/identityToken/apiTokenRequest"; +import { PasswordTokenRequest } from "../../models/request/identityToken/passwordTokenRequest"; +import { SsoTokenRequest } from "../../models/request/identityToken/ssoTokenRequest"; +import { TokenRequestTwoFactor } from "../../models/request/identityToken/tokenRequestTwoFactor"; +import { KeysRequest } from "../../models/request/keysRequest"; +import { IdentityCaptchaResponse } from "../../models/response/identityCaptchaResponse"; +import { IdentityTokenResponse } from "../../models/response/identityTokenResponse"; +import { IdentityTwoFactorResponse } from "../../models/response/identityTwoFactorResponse"; + +export abstract class LogInStrategy { + protected abstract tokenRequest: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest; + protected captchaBypassToken: string = null; + + constructor( + protected cryptoService: CryptoService, + protected apiService: ApiService, + protected tokenService: TokenService, + protected appIdService: AppIdService, + protected platformUtilsService: PlatformUtilsService, + protected messagingService: MessagingService, + protected logService: LogService, + protected stateService: StateService, + protected twoFactorService: TwoFactorService + ) {} + + abstract logIn( + credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + ): Promise; + + async logInTwoFactor( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string = null + ): Promise { + this.tokenRequest.setTwoFactor(twoFactor); + return this.startLogIn(); + } + + protected async startLogIn(): Promise { + this.twoFactorService.clearSelectedProvider(); + + const response = await this.apiService.postIdentityToken(this.tokenRequest); + + if (response instanceof IdentityTwoFactorResponse) { + return this.processTwoFactorResponse(response); + } else if (response instanceof IdentityCaptchaResponse) { + return this.processCaptchaResponse(response); + } else if (response instanceof IdentityTokenResponse) { + return this.processTokenResponse(response); + } + + throw new Error("Invalid response object."); + } + + protected onSuccessfulLogin(response: IdentityTokenResponse): Promise { + // Implemented in subclass if required + return null; + } + + protected async buildDeviceRequest() { + const appId = await this.appIdService.getAppId(); + return new DeviceRequest(appId, this.platformUtilsService); + } + + protected async buildTwoFactor(userProvidedTwoFactor?: TokenRequestTwoFactor) { + if (userProvidedTwoFactor != null) { + return userProvidedTwoFactor; + } + + const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(); + if (storedTwoFactorToken != null) { + return new TokenRequestTwoFactor(TwoFactorProviderType.Remember, storedTwoFactorToken, false); + } + + return new TokenRequestTwoFactor(); + } + + protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) { + const accountInformation = await this.tokenService.decodeToken(tokenResponse.accessToken); + await this.stateService.addAccount( + new Account({ + profile: { + ...new AccountProfile(), + ...{ + userId: accountInformation.sub, + email: accountInformation.email, + hasPremiumPersonally: accountInformation.premium, + kdfIterations: tokenResponse.kdfIterations, + kdfType: tokenResponse.kdf, + }, + }, + tokens: { + ...new AccountTokens(), + ...{ + accessToken: tokenResponse.accessToken, + refreshToken: tokenResponse.refreshToken, + }, + }, + }) + ); + } + + protected async processTokenResponse(response: IdentityTokenResponse): Promise { + const result = new AuthResult(); + result.resetMasterPassword = response.resetMasterPassword; + result.forcePasswordReset = response.forcePasswordReset; + + await this.saveAccountInformation(response); + + if (response.twoFactorToken != null) { + await this.tokenService.setTwoFactorToken(response); + } + + const newSsoUser = response.key == null; + if (!newSsoUser) { + await this.cryptoService.setEncKey(response.key); + await this.cryptoService.setEncPrivateKey( + response.privateKey ?? (await this.createKeyPairForOldAccount()) + ); + } + + await this.onSuccessfulLogin(response); + + await this.stateService.setBiometricLocked(false); + this.messagingService.send("loggedIn"); + + return result; + } + + private async processTwoFactorResponse(response: IdentityTwoFactorResponse): Promise { + const result = new AuthResult(); + result.twoFactorProviders = response.twoFactorProviders2; + this.twoFactorService.setProviders(response); + this.captchaBypassToken = response.captchaToken ?? null; + return result; + } + + private async processCaptchaResponse(response: IdentityCaptchaResponse): Promise { + const result = new AuthResult(); + result.captchaSiteKey = response.siteKey; + return result; + } + + private async createKeyPairForOldAccount() { + try { + const [publicKey, privateKey] = await this.cryptoService.makeKeyPair(); + await this.apiService.postAccountKeys(new KeysRequest(publicKey, privateKey.encryptedString)); + return privateKey.encryptedString; + } catch (e) { + this.logService.error(e); + } + } +} diff --git a/jslib/common/src/misc/logInStrategies/passwordLogin.strategy.ts b/jslib/common/src/misc/logInStrategies/passwordLogin.strategy.ts new file mode 100644 index 00000000..81947e56 --- /dev/null +++ b/jslib/common/src/misc/logInStrategies/passwordLogin.strategy.ts @@ -0,0 +1,95 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { AuthService } from "../../abstractions/auth.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { HashPurpose } from "../../enums/hashPurpose"; +import { AuthResult } from "../../models/domain/authResult"; +import { PasswordLogInCredentials } from "../../models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "../../models/domain/symmetricCryptoKey"; +import { PasswordTokenRequest } from "../../models/request/identityToken/passwordTokenRequest"; +import { TokenRequestTwoFactor } from "../../models/request/identityToken/tokenRequestTwoFactor"; + +import { LogInStrategy } from "./logIn.strategy"; + +export class PasswordLogInStrategy extends LogInStrategy { + get email() { + return this.tokenRequest.email; + } + + get masterPasswordHash() { + return this.tokenRequest.masterPasswordHash; + } + + tokenRequest: PasswordTokenRequest; + + private localHashedPassword: string; + private key: SymmetricCryptoKey; + + constructor( + cryptoService: CryptoService, + apiService: ApiService, + tokenService: TokenService, + appIdService: AppIdService, + platformUtilsService: PlatformUtilsService, + messagingService: MessagingService, + logService: LogService, + stateService: StateService, + twoFactorService: TwoFactorService, + private authService: AuthService + ) { + super( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService + ); + } + + async onSuccessfulLogin() { + await this.cryptoService.setKey(this.key); + await this.cryptoService.setKeyHash(this.localHashedPassword); + } + + async logInTwoFactor( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string + ): Promise { + this.tokenRequest.captchaResponse = captchaResponse ?? this.captchaBypassToken; + return super.logInTwoFactor(twoFactor); + } + + async logIn(credentials: PasswordLogInCredentials) { + const { email, masterPassword, captchaToken, twoFactor } = credentials; + + this.key = await this.authService.makePreloginKey(masterPassword, email); + + // Hash the password early (before authentication) so we don't persist it in memory in plaintext + this.localHashedPassword = await this.cryptoService.hashPassword( + masterPassword, + this.key, + HashPurpose.LocalAuthorization + ); + const hashedPassword = await this.cryptoService.hashPassword(masterPassword, this.key); + + this.tokenRequest = new PasswordTokenRequest( + email, + hashedPassword, + captchaToken, + await this.buildTwoFactor(twoFactor), + await this.buildDeviceRequest() + ); + + return this.startLogIn(); + } +} diff --git a/jslib/common/src/misc/logInStrategies/ssoLogin.strategy.ts b/jslib/common/src/misc/logInStrategies/ssoLogin.strategy.ts new file mode 100644 index 00000000..e56bd7a3 --- /dev/null +++ b/jslib/common/src/misc/logInStrategies/ssoLogin.strategy.ts @@ -0,0 +1,70 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { KeyConnectorService } from "../../abstractions/keyConnector.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { SsoLogInCredentials } from "../../models/domain/logInCredentials"; +import { SsoTokenRequest } from "../../models/request/identityToken/ssoTokenRequest"; +import { IdentityTokenResponse } from "../../models/response/identityTokenResponse"; + +import { LogInStrategy } from "./logIn.strategy"; + +export class SsoLogInStrategy extends LogInStrategy { + tokenRequest: SsoTokenRequest; + orgId: string; + + constructor( + cryptoService: CryptoService, + apiService: ApiService, + tokenService: TokenService, + appIdService: AppIdService, + platformUtilsService: PlatformUtilsService, + messagingService: MessagingService, + logService: LogService, + stateService: StateService, + twoFactorService: TwoFactorService, + private keyConnectorService: KeyConnectorService + ) { + super( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService + ); + } + + async onSuccessfulLogin(tokenResponse: IdentityTokenResponse) { + const newSsoUser = tokenResponse.key == null; + + if (tokenResponse.keyConnectorUrl != null) { + if (!newSsoUser) { + await this.keyConnectorService.getAndSetKey(tokenResponse.keyConnectorUrl); + } else { + await this.keyConnectorService.convertNewSsoUserToKeyConnector(tokenResponse, this.orgId); + } + } + } + + async logIn(credentials: SsoLogInCredentials) { + this.orgId = credentials.orgId; + this.tokenRequest = new SsoTokenRequest( + credentials.code, + credentials.codeVerifier, + credentials.redirectUrl, + await this.buildTwoFactor(credentials.twoFactor), + await this.buildDeviceRequest() + ); + + return this.startLogIn(); + } +} diff --git a/jslib/common/src/misc/nodeUtils.ts b/jslib/common/src/misc/nodeUtils.ts new file mode 100644 index 00000000..5b5ba27b --- /dev/null +++ b/jslib/common/src/misc/nodeUtils.ts @@ -0,0 +1,34 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as readline from "readline"; + +export class NodeUtils { + static mkdirpSync(targetDir: string, mode = "700", relative = false, relativeDir: string = null) { + const initialDir = path.isAbsolute(targetDir) ? path.sep : ""; + const baseDir = relative ? (relativeDir != null ? relativeDir : __dirname) : "."; + targetDir.split(path.sep).reduce((parentDir, childDir) => { + const dir = path.resolve(baseDir, parentDir, childDir); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, mode); + } + return dir; + }, initialDir); + } + static readFirstLine(fileName: string) { + return new Promise((resolve, reject) => { + const readStream = fs.createReadStream(fileName, { encoding: "utf8" }); + const readInterface = readline.createInterface(readStream); + readInterface + .on("line", (line) => { + readStream.close(); + resolve(line); + }) + .on("error", (err) => reject(err)); + }); + } + + // https://stackoverflow.com/a/31394257 + static bufferToArrayBuffer(buf: Buffer): ArrayBuffer { + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + } +} diff --git a/jslib/common/src/misc/sequentialize.ts b/jslib/common/src/misc/sequentialize.ts new file mode 100644 index 00000000..c2421147 --- /dev/null +++ b/jslib/common/src/misc/sequentialize.ts @@ -0,0 +1,57 @@ +/** + * Use as a Decorator on async functions, it will prevent multiple 'active' calls as the same time + * + * If a promise was returned from a previous call to this function, that hasn't yet resolved it will + * be returned, instead of calling the original function again + * + * Results are not cached, once the promise has returned, the next call will result in a fresh call + * + * Read more at https://github.com/bitwarden/jslib/pull/7 + */ +export function sequentialize(cacheKey: (args: any[]) => string) { + return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { + const originalMethod: () => Promise = descriptor.value; + const caches = new Map>>(); + + const getCache = (obj: any) => { + let cache = caches.get(obj); + if (cache != null) { + return cache; + } + cache = new Map>(); + caches.set(obj, cache); + return cache; + }; + + return { + value: function (...args: any[]) { + const cache = getCache(this); + const argsCacheKey = cacheKey(args); + let response = cache.get(argsCacheKey); + if (response != null) { + return response; + } + + const onFinally = () => { + cache.delete(argsCacheKey); + if (cache.size === 0) { + caches.delete(this); + } + }; + response = originalMethod + .apply(this, args) + .then((val: any) => { + onFinally(); + return val; + }) + .catch((err: any) => { + onFinally(); + throw err; + }); + + cache.set(argsCacheKey, response); + return response; + }, + }; + }; +} diff --git a/jslib/common/src/misc/serviceUtils.ts b/jslib/common/src/misc/serviceUtils.ts new file mode 100644 index 00000000..b6c05092 --- /dev/null +++ b/jslib/common/src/misc/serviceUtils.ts @@ -0,0 +1,72 @@ +import { ITreeNodeObject, TreeNode } from "../models/domain/treeNode"; + +export class ServiceUtils { + static nestedTraverse( + nodeTree: TreeNode[], + partIndex: number, + parts: string[], + obj: ITreeNodeObject, + parent: ITreeNodeObject, + delimiter: string + ) { + if (parts.length <= partIndex) { + return; + } + + const end = partIndex === parts.length - 1; + const partName = parts[partIndex]; + + for (let i = 0; i < nodeTree.length; i++) { + if (nodeTree[i].node.name !== parts[partIndex]) { + continue; + } + if (end && nodeTree[i].node.id !== obj.id) { + // Another node with the same name. + nodeTree.push(new TreeNode(obj, partName, parent)); + return; + } + ServiceUtils.nestedTraverse( + nodeTree[i].children, + partIndex + 1, + parts, + obj, + nodeTree[i].node, + delimiter + ); + return; + } + + if (nodeTree.filter((n) => n.node.name === partName).length === 0) { + if (end) { + nodeTree.push(new TreeNode(obj, partName, parent)); + return; + } + const newPartName = parts[partIndex] + delimiter + parts[partIndex + 1]; + ServiceUtils.nestedTraverse( + nodeTree, + 0, + [newPartName, ...parts.slice(partIndex + 2)], + obj, + parent, + delimiter + ); + } + } + + static getTreeNodeObject( + nodeTree: TreeNode[], + id: string + ): TreeNode { + for (let i = 0; i < nodeTree.length; i++) { + if (nodeTree[i].node.id === id) { + return nodeTree[i]; + } else if (nodeTree[i].children != null) { + const node = ServiceUtils.getTreeNodeObject(nodeTree[i].children, id); + if (node !== null) { + return node; + } + } + } + return null; + } +} diff --git a/jslib/common/src/misc/throttle.ts b/jslib/common/src/misc/throttle.ts new file mode 100644 index 00000000..852ee999 --- /dev/null +++ b/jslib/common/src/misc/throttle.ts @@ -0,0 +1,69 @@ +/** + * Use as a Decorator on async functions, it will limit how many times the function can be + * in-flight at a time. + * + * Calls beyond the limit will be queued, and run when one of the active calls finishes + */ +export function throttle(limit: number, throttleKey: (args: any[]) => string) { + return ( + target: any, + propertyKey: string | symbol, + descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise> + ) => { + const originalMethod: () => Promise = descriptor.value; + const allThrottles = new Map void)[]>>(); + + const getThrottles = (obj: any) => { + let throttles = allThrottles.get(obj); + if (throttles != null) { + return throttles; + } + throttles = new Map void)[]>(); + allThrottles.set(obj, throttles); + return throttles; + }; + + return { + value: function (...args: any[]) { + const throttles = getThrottles(this); + const argsThrottleKey = throttleKey(args); + let queue = throttles.get(argsThrottleKey); + if (queue == null) { + queue = []; + throttles.set(argsThrottleKey, queue); + } + + return new Promise((resolve, reject) => { + const exec = () => { + const onFinally = () => { + queue.splice(queue.indexOf(exec), 1); + if (queue.length >= limit) { + queue[limit - 1](); + } else if (queue.length === 0) { + throttles.delete(argsThrottleKey); + if (throttles.size === 0) { + allThrottles.delete(this); + } + } + }; + originalMethod + .apply(this, args) + .then((val: any) => { + onFinally(); + return val; + }) + .catch((err: any) => { + onFinally(); + throw err; + }) + .then(resolve, reject); + }; + queue.push(exec); + if (queue.length <= limit) { + exec(); + } + }); + }, + }; + }; +} diff --git a/jslib/common/src/misc/tldjs.noop.ts b/jslib/common/src/misc/tldjs.noop.ts new file mode 100644 index 00000000..b6273a61 --- /dev/null +++ b/jslib/common/src/misc/tldjs.noop.ts @@ -0,0 +1,7 @@ +export function getDomain(host: string): string | null { + return null; +} + +export function isValid(host: string): boolean { + return true; +} diff --git a/jslib/common/src/misc/utils.ts b/jslib/common/src/misc/utils.ts new file mode 100644 index 00000000..1d96e082 --- /dev/null +++ b/jslib/common/src/misc/utils.ts @@ -0,0 +1,406 @@ +/* eslint-disable no-useless-escape */ +import * as tldjs from "tldjs"; + +import { I18nService } from "../abstractions/i18n.service"; + +const nodeURL = typeof window === "undefined" ? require("url") : null; + +export class Utils { + static inited = false; + static isNode = false; + static isBrowser = true; + static isMobileBrowser = false; + static isAppleMobileBrowser = false; + static global: any = null; + static tldEndingRegex = + /.*\.(com|net|org|edu|uk|gov|ca|de|jp|fr|au|ru|ch|io|es|us|co|xyz|info|ly|mil)$/; + // Transpiled version of /\p{Emoji_Presentation}/gu using https://mothereff.in/regexpu. Used for compatability in older browsers. + static regexpEmojiPresentation = + /(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])/g; + + static init() { + if (Utils.inited) { + return; + } + + Utils.inited = true; + Utils.isNode = + typeof process !== "undefined" && + (process as any).release != null && + (process as any).release.name === "node"; + Utils.isBrowser = typeof window !== "undefined"; + Utils.isMobileBrowser = Utils.isBrowser && this.isMobile(window); + Utils.isAppleMobileBrowser = Utils.isBrowser && this.isAppleMobile(window); + Utils.global = Utils.isNode && !Utils.isBrowser ? global : window; + } + + static fromB64ToArray(str: string): Uint8Array { + if (Utils.isNode) { + return new Uint8Array(Buffer.from(str, "base64")); + } else { + const binaryString = window.atob(str); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; + } + } + + static fromUrlB64ToArray(str: string): Uint8Array { + return Utils.fromB64ToArray(Utils.fromUrlB64ToB64(str)); + } + + static fromHexToArray(str: string): Uint8Array { + if (Utils.isNode) { + return new Uint8Array(Buffer.from(str, "hex")); + } else { + const bytes = new Uint8Array(str.length / 2); + for (let i = 0; i < str.length; i += 2) { + bytes[i / 2] = parseInt(str.substr(i, 2), 16); + } + return bytes; + } + } + + static fromUtf8ToArray(str: string): Uint8Array { + if (Utils.isNode) { + return new Uint8Array(Buffer.from(str, "utf8")); + } else { + const strUtf8 = unescape(encodeURIComponent(str)); + const arr = new Uint8Array(strUtf8.length); + for (let i = 0; i < strUtf8.length; i++) { + arr[i] = strUtf8.charCodeAt(i); + } + return arr; + } + } + + static fromByteStringToArray(str: string): Uint8Array { + const arr = new Uint8Array(str.length); + for (let i = 0; i < str.length; i++) { + arr[i] = str.charCodeAt(i); + } + return arr; + } + + static fromBufferToB64(buffer: ArrayBuffer): string { + if (Utils.isNode) { + return Buffer.from(buffer).toString("base64"); + } else { + let binary = ""; + const bytes = new Uint8Array(buffer); + for (let i = 0; i < bytes.byteLength; i++) { + binary += String.fromCharCode(bytes[i]); + } + return window.btoa(binary); + } + } + + static fromBufferToUrlB64(buffer: ArrayBuffer): string { + return Utils.fromB64toUrlB64(Utils.fromBufferToB64(buffer)); + } + + static fromB64toUrlB64(b64Str: string) { + return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + } + + static fromBufferToUtf8(buffer: ArrayBuffer): string { + if (Utils.isNode) { + return Buffer.from(buffer).toString("utf8"); + } else { + const bytes = new Uint8Array(buffer); + const encodedString = String.fromCharCode.apply(null, bytes); + return decodeURIComponent(escape(encodedString)); + } + } + + static fromBufferToByteString(buffer: ArrayBuffer): string { + return String.fromCharCode.apply(null, new Uint8Array(buffer)); + } + + // ref: https://stackoverflow.com/a/40031979/1090359 + static fromBufferToHex(buffer: ArrayBuffer): string { + if (Utils.isNode) { + return Buffer.from(buffer).toString("hex"); + } else { + const bytes = new Uint8Array(buffer); + return Array.prototype.map + .call(bytes, (x: number) => ("00" + x.toString(16)).slice(-2)) + .join(""); + } + } + + static fromUrlB64ToB64(urlB64Str: string): string { + let output = urlB64Str.replace(/-/g, "+").replace(/_/g, "/"); + switch (output.length % 4) { + case 0: + break; + case 2: + output += "=="; + break; + case 3: + output += "="; + break; + default: + throw new Error("Illegal base64url string!"); + } + + return output; + } + + static fromUrlB64ToUtf8(urlB64Str: string): string { + return Utils.fromB64ToUtf8(Utils.fromUrlB64ToB64(urlB64Str)); + } + + static fromUtf8ToB64(utfStr: string): string { + if (Utils.isNode) { + return Buffer.from(utfStr, "utf8").toString("base64"); + } else { + return decodeURIComponent(escape(window.btoa(utfStr))); + } + } + + static fromUtf8ToUrlB64(utfStr: string): string { + return Utils.fromBufferToUrlB64(Utils.fromUtf8ToArray(utfStr)); + } + + static fromB64ToUtf8(b64Str: string): string { + if (Utils.isNode) { + return Buffer.from(b64Str, "base64").toString("utf8"); + } else { + return decodeURIComponent(escape(window.atob(b64Str))); + } + } + + // ref: http://stackoverflow.com/a/2117523/1090359 + static newGuid(): string { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + } + + static isGuid(id: string) { + return RegExp( + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, + "i" + ).test(id); + } + + static getHostname(uriString: string): string { + const url = Utils.getUrl(uriString); + try { + return url != null && url.hostname !== "" ? url.hostname : null; + } catch { + return null; + } + } + + static getHost(uriString: string): string { + const url = Utils.getUrl(uriString); + try { + return url != null && url.host !== "" ? url.host : null; + } catch { + return null; + } + } + + static getDomain(uriString: string): string { + if (uriString == null) { + return null; + } + + uriString = uriString.trim(); + if (uriString === "") { + return null; + } + + if (uriString.startsWith("data:")) { + return null; + } + + let httpUrl = uriString.startsWith("http://") || uriString.startsWith("https://"); + if ( + !httpUrl && + uriString.indexOf("://") < 0 && + Utils.tldEndingRegex.test(uriString) && + uriString.indexOf("@") < 0 + ) { + uriString = "http://" + uriString; + httpUrl = true; + } + + if (httpUrl) { + try { + const url = Utils.getUrlObject(uriString); + const validHostname = tldjs?.isValid != null ? tldjs.isValid(url.hostname) : true; + if (!validHostname) { + return null; + } + + if (url.hostname === "localhost" || Utils.validIpAddress(url.hostname)) { + return url.hostname; + } + + const urlDomain = + tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(url.hostname) : null; + return urlDomain != null ? urlDomain : url.hostname; + } catch (e) { + // Invalid domain, try another approach below. + } + } + + try { + const domain = tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(uriString) : null; + + if (domain != null) { + return domain; + } + } catch { + return null; + } + + return null; + } + + static getQueryParams(uriString: string): Map { + const url = Utils.getUrl(uriString); + if (url == null || url.search == null || url.search === "") { + return null; + } + const map = new Map(); + const pairs = (url.search[0] === "?" ? url.search.substr(1) : url.search).split("&"); + pairs.forEach((pair) => { + const parts = pair.split("="); + if (parts.length < 1) { + return; + } + map.set( + decodeURIComponent(parts[0]).toLowerCase(), + parts[1] == null ? "" : decodeURIComponent(parts[1]) + ); + }); + return map; + } + + static getSortFunction(i18nService: I18nService, prop: string) { + return (a: any, b: any) => { + if (a[prop] == null && b[prop] != null) { + return -1; + } + if (a[prop] != null && b[prop] == null) { + return 1; + } + if (a[prop] == null && b[prop] == null) { + return 0; + } + + return i18nService.collator + ? i18nService.collator.compare(a[prop], b[prop]) + : a[prop].localeCompare(b[prop]); + }; + } + + static isNullOrWhitespace(str: string): boolean { + return str == null || typeof str !== "string" || str.trim() === ""; + } + + static isNullOrEmpty(str: string): boolean { + return str == null || typeof str !== "string" || str == ""; + } + + static nameOf(name: string & keyof T) { + return name; + } + + static assign(target: T, source: Partial): T { + return Object.assign(target, source); + } + + static iterateEnum(obj: O) { + return (Object.keys(obj).filter((k) => Number.isNaN(+k)) as K[]).map((k) => obj[k]); + } + + static getUrl(uriString: string): URL { + if (uriString == null) { + return null; + } + + uriString = uriString.trim(); + if (uriString === "") { + return null; + } + + let url = Utils.getUrlObject(uriString); + if (url == null) { + const hasHttpProtocol = + uriString.indexOf("http://") === 0 || uriString.indexOf("https://") === 0; + if (!hasHttpProtocol && uriString.indexOf(".") > -1) { + url = Utils.getUrlObject("http://" + uriString); + } + } + return url; + } + + static camelToPascalCase(s: string) { + return s.charAt(0).toUpperCase() + s.slice(1); + } + + private static validIpAddress(ipString: string): boolean { + const ipRegex = + /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + return ipRegex.test(ipString); + } + + private static isMobile(win: Window) { + let mobile = false; + ((a) => { + if ( + /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test( + a + ) || + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( + a.substr(0, 4) + ) + ) { + mobile = true; + } + })(win.navigator.userAgent || win.navigator.vendor || (win as any).opera); + return mobile || win.navigator.userAgent.match(/iPad/i) != null; + } + + private static isAppleMobile(win: Window) { + return ( + win.navigator.userAgent.match(/iPhone/i) != null || + win.navigator.userAgent.match(/iPad/i) != null + ); + } + + private static getUrlObject(uriString: string): URL { + try { + if (nodeURL != null) { + return new nodeURL.URL(uriString); + } else if (typeof URL === "function") { + return new URL(uriString); + } else if (window != null) { + const hasProtocol = uriString.indexOf("://") > -1; + if (!hasProtocol && uriString.indexOf(".") > -1) { + uriString = "http://" + uriString; + } else if (!hasProtocol) { + return null; + } + const anchor = window.document.createElement("a"); + anchor.href = uriString; + return anchor as any; + } + } catch (e) { + // Ignore error + } + + return null; + } +} + +Utils.init(); diff --git a/jslib/common/src/misc/webauthn_iframe.ts b/jslib/common/src/misc/webauthn_iframe.ts new file mode 100644 index 00000000..969dde04 --- /dev/null +++ b/jslib/common/src/misc/webauthn_iframe.ts @@ -0,0 +1,106 @@ +import { I18nService } from "../abstractions/i18n.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; + +export class WebAuthnIFrame { + private iframe: HTMLIFrameElement = null; + private connectorLink: HTMLAnchorElement; + private parseFunction = this.parseMessage.bind(this); + + constructor( + private win: Window, + private webVaultUrl: string, + private webAuthnNewTab: boolean, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private successCallback: Function, // eslint-disable-line + private errorCallback: Function, // eslint-disable-line + private infoCallback: Function // eslint-disable-line + ) { + this.connectorLink = win.document.createElement("a"); + } + + init(data: any): void { + const params = new URLSearchParams({ + data: this.base64Encode(JSON.stringify(data)), + parent: encodeURIComponent(this.win.document.location.href), + btnText: encodeURIComponent(this.i18nService.t("webAuthnAuthenticate")), + v: "1", + }); + + if (this.webAuthnNewTab) { + // Firefox fallback which opens the webauthn page in a new tab + params.append("locale", this.i18nService.translationLocale); + this.platformUtilsService.launchUri( + `${this.webVaultUrl}/webauthn-fallback-connector.html?${params}` + ); + } else { + this.connectorLink.href = `${this.webVaultUrl}/webauthn-connector.html?${params}`; + this.iframe = this.win.document.getElementById("webauthn_iframe") as HTMLIFrameElement; + this.iframe.allow = "publickey-credentials-get " + new URL(this.webVaultUrl).origin; + this.iframe.src = this.connectorLink.href; + + this.win.addEventListener("message", this.parseFunction, false); + } + } + + stop() { + this.sendMessage("stop"); + } + + start() { + this.sendMessage("start"); + } + + sendMessage(message: any) { + if (!this.iframe || !this.iframe.src || !this.iframe.contentWindow) { + return; + } + + this.iframe.contentWindow.postMessage(message, this.iframe.src); + } + + base64Encode(str: string): string { + return btoa( + encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => { + return String.fromCharCode(("0x" + p1) as any); + }) + ); + } + + cleanup() { + this.win.removeEventListener("message", this.parseFunction, false); + } + + private parseMessage(event: MessageEvent) { + if (!this.validMessage(event)) { + return; + } + + const parts: string[] = event.data.split("|"); + if (parts[0] === "success" && this.successCallback) { + this.successCallback(parts[1]); + } else if (parts[0] === "error" && this.errorCallback) { + this.errorCallback(parts[1]); + } else if (parts[0] === "info" && this.infoCallback) { + this.infoCallback(parts[1]); + } + } + + private validMessage(event: MessageEvent) { + if ( + event.origin == null || + event.origin === "" || + event.origin !== (this.connectorLink as any).origin || + event.data == null || + typeof event.data !== "string" + ) { + return false; + } + + return ( + event.data.indexOf("success|") === 0 || + event.data.indexOf("error|") === 0 || + event.data.indexOf("info|") === 0 + ); + } +} diff --git a/jslib/common/src/misc/wordlist.ts b/jslib/common/src/misc/wordlist.ts new file mode 100644 index 00000000..cdd30110 --- /dev/null +++ b/jslib/common/src/misc/wordlist.ts @@ -0,0 +1,7779 @@ +// EFF's Long Wordlist from https://www.eff.org/dice +export const EEFLongWordList = [ + "abacus", + "abdomen", + "abdominal", + "abide", + "abiding", + "ability", + "ablaze", + "able", + "abnormal", + "abrasion", + "abrasive", + "abreast", + "abridge", + "abroad", + "abruptly", + "absence", + "absentee", + "absently", + "absinthe", + "absolute", + "absolve", + "abstain", + "abstract", + "absurd", + "accent", + "acclaim", + "acclimate", + "accompany", + "account", + "accuracy", + "accurate", + "accustom", + "acetone", + "achiness", + "aching", + "acid", + "acorn", + "acquaint", + "acquire", + "acre", + "acrobat", + "acronym", + "acting", + "action", + "activate", + "activator", + "active", + "activism", + "activist", + "activity", + "actress", + "acts", + "acutely", + "acuteness", + "aeration", + "aerobics", + "aerosol", + "aerospace", + "afar", + "affair", + "affected", + "affecting", + "affection", + "affidavit", + "affiliate", + "affirm", + "affix", + "afflicted", + "affluent", + "afford", + "affront", + "aflame", + "afloat", + "aflutter", + "afoot", + "afraid", + "afterglow", + "afterlife", + "aftermath", + "aftermost", + "afternoon", + "aged", + "ageless", + "agency", + "agenda", + "agent", + "aggregate", + "aghast", + "agile", + "agility", + "aging", + "agnostic", + "agonize", + "agonizing", + "agony", + "agreeable", + "agreeably", + "agreed", + "agreeing", + "agreement", + "aground", + "ahead", + "ahoy", + "aide", + "aids", + "aim", + "ajar", + "alabaster", + "alarm", + "albatross", + "album", + "alfalfa", + "algebra", + "algorithm", + "alias", + "alibi", + "alienable", + "alienate", + "aliens", + "alike", + "alive", + "alkaline", + "alkalize", + "almanac", + "almighty", + "almost", + "aloe", + "aloft", + "aloha", + "alone", + "alongside", + "aloof", + "alphabet", + "alright", + "although", + "altitude", + "alto", + "aluminum", + "alumni", + "always", + "amaretto", + "amaze", + "amazingly", + "amber", + "ambiance", + "ambiguity", + "ambiguous", + "ambition", + "ambitious", + "ambulance", + "ambush", + "amendable", + "amendment", + "amends", + "amenity", + "amiable", + "amicably", + "amid", + "amigo", + "amino", + "amiss", + "ammonia", + "ammonium", + "amnesty", + "amniotic", + "among", + "amount", + "amperage", + "ample", + "amplifier", + "amplify", + "amply", + "amuck", + "amulet", + "amusable", + "amused", + "amusement", + "amuser", + "amusing", + "anaconda", + "anaerobic", + "anagram", + "anatomist", + "anatomy", + "anchor", + "anchovy", + "ancient", + "android", + "anemia", + "anemic", + "aneurism", + "anew", + "angelfish", + "angelic", + "anger", + "angled", + "angler", + "angles", + "angling", + "angrily", + "angriness", + "anguished", + "angular", + "animal", + "animate", + "animating", + "animation", + "animator", + "anime", + "animosity", + "ankle", + "annex", + "annotate", + "announcer", + "annoying", + "annually", + "annuity", + "anointer", + "another", + "answering", + "antacid", + "antarctic", + "anteater", + "antelope", + "antennae", + "anthem", + "anthill", + "anthology", + "antibody", + "antics", + "antidote", + "antihero", + "antiquely", + "antiques", + "antiquity", + "antirust", + "antitoxic", + "antitrust", + "antiviral", + "antivirus", + "antler", + "antonym", + "antsy", + "anvil", + "anybody", + "anyhow", + "anymore", + "anyone", + "anyplace", + "anything", + "anytime", + "anyway", + "anywhere", + "aorta", + "apache", + "apostle", + "appealing", + "appear", + "appease", + "appeasing", + "appendage", + "appendix", + "appetite", + "appetizer", + "applaud", + "applause", + "apple", + "appliance", + "applicant", + "applied", + "apply", + "appointee", + "appraisal", + "appraiser", + "apprehend", + "approach", + "approval", + "approve", + "apricot", + "april", + "apron", + "aptitude", + "aptly", + "aqua", + "aqueduct", + "arbitrary", + "arbitrate", + "ardently", + "area", + "arena", + "arguable", + "arguably", + "argue", + "arise", + "armadillo", + "armband", + "armchair", + "armed", + "armful", + "armhole", + "arming", + "armless", + "armoire", + "armored", + "armory", + "armrest", + "army", + "aroma", + "arose", + "around", + "arousal", + "arrange", + "array", + "arrest", + "arrival", + "arrive", + "arrogance", + "arrogant", + "arson", + "art", + "ascend", + "ascension", + "ascent", + "ascertain", + "ashamed", + "ashen", + "ashes", + "ashy", + "aside", + "askew", + "asleep", + "asparagus", + "aspect", + "aspirate", + "aspire", + "aspirin", + "astonish", + "astound", + "astride", + "astrology", + "astronaut", + "astronomy", + "astute", + "atlantic", + "atlas", + "atom", + "atonable", + "atop", + "atrium", + "atrocious", + "atrophy", + "attach", + "attain", + "attempt", + "attendant", + "attendee", + "attention", + "attentive", + "attest", + "attic", + "attire", + "attitude", + "attractor", + "attribute", + "atypical", + "auction", + "audacious", + "audacity", + "audible", + "audibly", + "audience", + "audio", + "audition", + "augmented", + "august", + "authentic", + "author", + "autism", + "autistic", + "autograph", + "automaker", + "automated", + "automatic", + "autopilot", + "available", + "avalanche", + "avatar", + "avenge", + "avenging", + "avenue", + "average", + "aversion", + "avert", + "aviation", + "aviator", + "avid", + "avoid", + "await", + "awaken", + "award", + "aware", + "awhile", + "awkward", + "awning", + "awoke", + "awry", + "axis", + "babble", + "babbling", + "babied", + "baboon", + "backache", + "backboard", + "backboned", + "backdrop", + "backed", + "backer", + "backfield", + "backfire", + "backhand", + "backing", + "backlands", + "backlash", + "backless", + "backlight", + "backlit", + "backlog", + "backpack", + "backpedal", + "backrest", + "backroom", + "backshift", + "backside", + "backslid", + "backspace", + "backspin", + "backstab", + "backstage", + "backtalk", + "backtrack", + "backup", + "backward", + "backwash", + "backwater", + "backyard", + "bacon", + "bacteria", + "bacterium", + "badass", + "badge", + "badland", + "badly", + "badness", + "baffle", + "baffling", + "bagel", + "bagful", + "baggage", + "bagged", + "baggie", + "bagginess", + "bagging", + "baggy", + "bagpipe", + "baguette", + "baked", + "bakery", + "bakeshop", + "baking", + "balance", + "balancing", + "balcony", + "balmy", + "balsamic", + "bamboo", + "banana", + "banish", + "banister", + "banjo", + "bankable", + "bankbook", + "banked", + "banker", + "banking", + "banknote", + "bankroll", + "banner", + "bannister", + "banshee", + "banter", + "barbecue", + "barbed", + "barbell", + "barber", + "barcode", + "barge", + "bargraph", + "barista", + "baritone", + "barley", + "barmaid", + "barman", + "barn", + "barometer", + "barrack", + "barracuda", + "barrel", + "barrette", + "barricade", + "barrier", + "barstool", + "bartender", + "barterer", + "bash", + "basically", + "basics", + "basil", + "basin", + "basis", + "basket", + "batboy", + "batch", + "bath", + "baton", + "bats", + "battalion", + "battered", + "battering", + "battery", + "batting", + "battle", + "bauble", + "bazooka", + "blabber", + "bladder", + "blade", + "blah", + "blame", + "blaming", + "blanching", + "blandness", + "blank", + "blaspheme", + "blasphemy", + "blast", + "blatancy", + "blatantly", + "blazer", + "blazing", + "bleach", + "bleak", + "bleep", + "blemish", + "blend", + "bless", + "blighted", + "blimp", + "bling", + "blinked", + "blinker", + "blinking", + "blinks", + "blip", + "blissful", + "blitz", + "blizzard", + "bloated", + "bloating", + "blob", + "blog", + "bloomers", + "blooming", + "blooper", + "blot", + "blouse", + "blubber", + "bluff", + "bluish", + "blunderer", + "blunt", + "blurb", + "blurred", + "blurry", + "blurt", + "blush", + "blustery", + "boaster", + "boastful", + "boasting", + "boat", + "bobbed", + "bobbing", + "bobble", + "bobcat", + "bobsled", + "bobtail", + "bodacious", + "body", + "bogged", + "boggle", + "bogus", + "boil", + "bok", + "bolster", + "bolt", + "bonanza", + "bonded", + "bonding", + "bondless", + "boned", + "bonehead", + "boneless", + "bonelike", + "boney", + "bonfire", + "bonnet", + "bonsai", + "bonus", + "bony", + "boogeyman", + "boogieman", + "book", + "boondocks", + "booted", + "booth", + "bootie", + "booting", + "bootlace", + "bootleg", + "boots", + "boozy", + "borax", + "boring", + "borough", + "borrower", + "borrowing", + "boss", + "botanical", + "botanist", + "botany", + "botch", + "both", + "bottle", + "bottling", + "bottom", + "bounce", + "bouncing", + "bouncy", + "bounding", + "boundless", + "bountiful", + "bovine", + "boxcar", + "boxer", + "boxing", + "boxlike", + "boxy", + "breach", + "breath", + "breeches", + "breeching", + "breeder", + "breeding", + "breeze", + "breezy", + "brethren", + "brewery", + "brewing", + "briar", + "bribe", + "brick", + "bride", + "bridged", + "brigade", + "bright", + "brilliant", + "brim", + "bring", + "brink", + "brisket", + "briskly", + "briskness", + "bristle", + "brittle", + "broadband", + "broadcast", + "broaden", + "broadly", + "broadness", + "broadside", + "broadways", + "broiler", + "broiling", + "broken", + "broker", + "bronchial", + "bronco", + "bronze", + "bronzing", + "brook", + "broom", + "brought", + "browbeat", + "brownnose", + "browse", + "browsing", + "bruising", + "brunch", + "brunette", + "brunt", + "brush", + "brussels", + "brute", + "brutishly", + "bubble", + "bubbling", + "bubbly", + "buccaneer", + "bucked", + "bucket", + "buckle", + "buckshot", + "buckskin", + "bucktooth", + "buckwheat", + "buddhism", + "buddhist", + "budding", + "buddy", + "budget", + "buffalo", + "buffed", + "buffer", + "buffing", + "buffoon", + "buggy", + "bulb", + "bulge", + "bulginess", + "bulgur", + "bulk", + "bulldog", + "bulldozer", + "bullfight", + "bullfrog", + "bullhorn", + "bullion", + "bullish", + "bullpen", + "bullring", + "bullseye", + "bullwhip", + "bully", + "bunch", + "bundle", + "bungee", + "bunion", + "bunkbed", + "bunkhouse", + "bunkmate", + "bunny", + "bunt", + "busboy", + "bush", + "busily", + "busload", + "bust", + "busybody", + "buzz", + "cabana", + "cabbage", + "cabbie", + "cabdriver", + "cable", + "caboose", + "cache", + "cackle", + "cacti", + "cactus", + "caddie", + "caddy", + "cadet", + "cadillac", + "cadmium", + "cage", + "cahoots", + "cake", + "calamari", + "calamity", + "calcium", + "calculate", + "calculus", + "caliber", + "calibrate", + "calm", + "caloric", + "calorie", + "calzone", + "camcorder", + "cameo", + "camera", + "camisole", + "camper", + "campfire", + "camping", + "campsite", + "campus", + "canal", + "canary", + "cancel", + "candied", + "candle", + "candy", + "cane", + "canine", + "canister", + "cannabis", + "canned", + "canning", + "cannon", + "cannot", + "canola", + "canon", + "canopener", + "canopy", + "canteen", + "canyon", + "capable", + "capably", + "capacity", + "cape", + "capillary", + "capital", + "capitol", + "capped", + "capricorn", + "capsize", + "capsule", + "caption", + "captivate", + "captive", + "captivity", + "capture", + "caramel", + "carat", + "caravan", + "carbon", + "cardboard", + "carded", + "cardiac", + "cardigan", + "cardinal", + "cardstock", + "carefully", + "caregiver", + "careless", + "caress", + "caretaker", + "cargo", + "caring", + "carless", + "carload", + "carmaker", + "carnage", + "carnation", + "carnival", + "carnivore", + "carol", + "carpenter", + "carpentry", + "carpool", + "carport", + "carried", + "carrot", + "carrousel", + "carry", + "cartel", + "cartload", + "carton", + "cartoon", + "cartridge", + "cartwheel", + "carve", + "carving", + "carwash", + "cascade", + "case", + "cash", + "casing", + "casino", + "casket", + "cassette", + "casually", + "casualty", + "catacomb", + "catalog", + "catalyst", + "catalyze", + "catapult", + "cataract", + "catatonic", + "catcall", + "catchable", + "catcher", + "catching", + "catchy", + "caterer", + "catering", + "catfight", + "catfish", + "cathedral", + "cathouse", + "catlike", + "catnap", + "catnip", + "catsup", + "cattail", + "cattishly", + "cattle", + "catty", + "catwalk", + "caucasian", + "caucus", + "causal", + "causation", + "cause", + "causing", + "cauterize", + "caution", + "cautious", + "cavalier", + "cavalry", + "caviar", + "cavity", + "cedar", + "celery", + "celestial", + "celibacy", + "celibate", + "celtic", + "cement", + "census", + "ceramics", + "ceremony", + "certainly", + "certainty", + "certified", + "certify", + "cesarean", + "cesspool", + "chafe", + "chaffing", + "chain", + "chair", + "chalice", + "challenge", + "chamber", + "chamomile", + "champion", + "chance", + "change", + "channel", + "chant", + "chaos", + "chaperone", + "chaplain", + "chapped", + "chaps", + "chapter", + "character", + "charbroil", + "charcoal", + "charger", + "charging", + "chariot", + "charity", + "charm", + "charred", + "charter", + "charting", + "chase", + "chasing", + "chaste", + "chastise", + "chastity", + "chatroom", + "chatter", + "chatting", + "chatty", + "cheating", + "cheddar", + "cheek", + "cheer", + "cheese", + "cheesy", + "chef", + "chemicals", + "chemist", + "chemo", + "cherisher", + "cherub", + "chess", + "chest", + "chevron", + "chevy", + "chewable", + "chewer", + "chewing", + "chewy", + "chief", + "chihuahua", + "childcare", + "childhood", + "childish", + "childless", + "childlike", + "chili", + "chill", + "chimp", + "chip", + "chirping", + "chirpy", + "chitchat", + "chivalry", + "chive", + "chloride", + "chlorine", + "choice", + "chokehold", + "choking", + "chomp", + "chooser", + "choosing", + "choosy", + "chop", + "chosen", + "chowder", + "chowtime", + "chrome", + "chubby", + "chuck", + "chug", + "chummy", + "chump", + "chunk", + "churn", + "chute", + "cider", + "cilantro", + "cinch", + "cinema", + "cinnamon", + "circle", + "circling", + "circular", + "circulate", + "circus", + "citable", + "citadel", + "citation", + "citizen", + "citric", + "citrus", + "city", + "civic", + "civil", + "clad", + "claim", + "clambake", + "clammy", + "clamor", + "clamp", + "clamshell", + "clang", + "clanking", + "clapped", + "clapper", + "clapping", + "clarify", + "clarinet", + "clarity", + "clash", + "clasp", + "class", + "clatter", + "clause", + "clavicle", + "claw", + "clay", + "clean", + "clear", + "cleat", + "cleaver", + "cleft", + "clench", + "clergyman", + "clerical", + "clerk", + "clever", + "clicker", + "client", + "climate", + "climatic", + "cling", + "clinic", + "clinking", + "clip", + "clique", + "cloak", + "clobber", + "clock", + "clone", + "cloning", + "closable", + "closure", + "clothes", + "clothing", + "cloud", + "clover", + "clubbed", + "clubbing", + "clubhouse", + "clump", + "clumsily", + "clumsy", + "clunky", + "clustered", + "clutch", + "clutter", + "coach", + "coagulant", + "coastal", + "coaster", + "coasting", + "coastland", + "coastline", + "coat", + "coauthor", + "cobalt", + "cobbler", + "cobweb", + "cocoa", + "coconut", + "cod", + "coeditor", + "coerce", + "coexist", + "coffee", + "cofounder", + "cognition", + "cognitive", + "cogwheel", + "coherence", + "coherent", + "cohesive", + "coil", + "coke", + "cola", + "cold", + "coleslaw", + "coliseum", + "collage", + "collapse", + "collar", + "collected", + "collector", + "collide", + "collie", + "collision", + "colonial", + "colonist", + "colonize", + "colony", + "colossal", + "colt", + "coma", + "come", + "comfort", + "comfy", + "comic", + "coming", + "comma", + "commence", + "commend", + "comment", + "commerce", + "commode", + "commodity", + "commodore", + "common", + "commotion", + "commute", + "commuting", + "compacted", + "compacter", + "compactly", + "compactor", + "companion", + "company", + "compare", + "compel", + "compile", + "comply", + "component", + "composed", + "composer", + "composite", + "compost", + "composure", + "compound", + "compress", + "comprised", + "computer", + "computing", + "comrade", + "concave", + "conceal", + "conceded", + "concept", + "concerned", + "concert", + "conch", + "concierge", + "concise", + "conclude", + "concrete", + "concur", + "condense", + "condiment", + "condition", + "condone", + "conducive", + "conductor", + "conduit", + "cone", + "confess", + "confetti", + "confidant", + "confident", + "confider", + "confiding", + "configure", + "confined", + "confining", + "confirm", + "conflict", + "conform", + "confound", + "confront", + "confused", + "confusing", + "confusion", + "congenial", + "congested", + "congrats", + "congress", + "conical", + "conjoined", + "conjure", + "conjuror", + "connected", + "connector", + "consensus", + "consent", + "console", + "consoling", + "consonant", + "constable", + "constant", + "constrain", + "constrict", + "construct", + "consult", + "consumer", + "consuming", + "contact", + "container", + "contempt", + "contend", + "contented", + "contently", + "contents", + "contest", + "context", + "contort", + "contour", + "contrite", + "control", + "contusion", + "convene", + "convent", + "copartner", + "cope", + "copied", + "copier", + "copilot", + "coping", + "copious", + "copper", + "copy", + "coral", + "cork", + "cornball", + "cornbread", + "corncob", + "cornea", + "corned", + "corner", + "cornfield", + "cornflake", + "cornhusk", + "cornmeal", + "cornstalk", + "corny", + "coronary", + "coroner", + "corporal", + "corporate", + "corral", + "correct", + "corridor", + "corrode", + "corroding", + "corrosive", + "corsage", + "corset", + "cortex", + "cosigner", + "cosmetics", + "cosmic", + "cosmos", + "cosponsor", + "cost", + "cottage", + "cotton", + "couch", + "cough", + "could", + "countable", + "countdown", + "counting", + "countless", + "country", + "county", + "courier", + "covenant", + "cover", + "coveted", + "coveting", + "coyness", + "cozily", + "coziness", + "cozy", + "crabbing", + "crabgrass", + "crablike", + "crabmeat", + "cradle", + "cradling", + "crafter", + "craftily", + "craftsman", + "craftwork", + "crafty", + "cramp", + "cranberry", + "crane", + "cranial", + "cranium", + "crank", + "crate", + "crave", + "craving", + "crawfish", + "crawlers", + "crawling", + "crayfish", + "crayon", + "crazed", + "crazily", + "craziness", + "crazy", + "creamed", + "creamer", + "creamlike", + "crease", + "creasing", + "creatable", + "create", + "creation", + "creative", + "creature", + "credible", + "credibly", + "credit", + "creed", + "creme", + "creole", + "crepe", + "crept", + "crescent", + "crested", + "cresting", + "crestless", + "crevice", + "crewless", + "crewman", + "crewmate", + "crib", + "cricket", + "cried", + "crier", + "crimp", + "crimson", + "cringe", + "cringing", + "crinkle", + "crinkly", + "crisped", + "crisping", + "crisply", + "crispness", + "crispy", + "criteria", + "critter", + "croak", + "crock", + "crook", + "croon", + "crop", + "cross", + "crouch", + "crouton", + "crowbar", + "crowd", + "crown", + "crucial", + "crudely", + "crudeness", + "cruelly", + "cruelness", + "cruelty", + "crumb", + "crummiest", + "crummy", + "crumpet", + "crumpled", + "cruncher", + "crunching", + "crunchy", + "crusader", + "crushable", + "crushed", + "crusher", + "crushing", + "crust", + "crux", + "crying", + "cryptic", + "crystal", + "cubbyhole", + "cube", + "cubical", + "cubicle", + "cucumber", + "cuddle", + "cuddly", + "cufflink", + "culinary", + "culminate", + "culpable", + "culprit", + "cultivate", + "cultural", + "culture", + "cupbearer", + "cupcake", + "cupid", + "cupped", + "cupping", + "curable", + "curator", + "curdle", + "cure", + "curfew", + "curing", + "curled", + "curler", + "curliness", + "curling", + "curly", + "curry", + "curse", + "cursive", + "cursor", + "curtain", + "curtly", + "curtsy", + "curvature", + "curve", + "curvy", + "cushy", + "cusp", + "cussed", + "custard", + "custodian", + "custody", + "customary", + "customer", + "customize", + "customs", + "cut", + "cycle", + "cyclic", + "cycling", + "cyclist", + "cylinder", + "cymbal", + "cytoplasm", + "cytoplast", + "dab", + "dad", + "daffodil", + "dagger", + "daily", + "daintily", + "dainty", + "dairy", + "daisy", + "dallying", + "dance", + "dancing", + "dandelion", + "dander", + "dandruff", + "dandy", + "danger", + "dangle", + "dangling", + "daredevil", + "dares", + "daringly", + "darkened", + "darkening", + "darkish", + "darkness", + "darkroom", + "darling", + "darn", + "dart", + "darwinism", + "dash", + "dastardly", + "data", + "datebook", + "dating", + "daughter", + "daunting", + "dawdler", + "dawn", + "daybed", + "daybreak", + "daycare", + "daydream", + "daylight", + "daylong", + "dayroom", + "daytime", + "dazzler", + "dazzling", + "deacon", + "deafening", + "deafness", + "dealer", + "dealing", + "dealmaker", + "dealt", + "dean", + "debatable", + "debate", + "debating", + "debit", + "debrief", + "debtless", + "debtor", + "debug", + "debunk", + "decade", + "decaf", + "decal", + "decathlon", + "decay", + "deceased", + "deceit", + "deceiver", + "deceiving", + "december", + "decency", + "decent", + "deception", + "deceptive", + "decibel", + "decidable", + "decimal", + "decimeter", + "decipher", + "deck", + "declared", + "decline", + "decode", + "decompose", + "decorated", + "decorator", + "decoy", + "decrease", + "decree", + "dedicate", + "dedicator", + "deduce", + "deduct", + "deed", + "deem", + "deepen", + "deeply", + "deepness", + "deface", + "defacing", + "defame", + "default", + "defeat", + "defection", + "defective", + "defendant", + "defender", + "defense", + "defensive", + "deferral", + "deferred", + "defiance", + "defiant", + "defile", + "defiling", + "define", + "definite", + "deflate", + "deflation", + "deflator", + "deflected", + "deflector", + "defog", + "deforest", + "defraud", + "defrost", + "deftly", + "defuse", + "defy", + "degraded", + "degrading", + "degrease", + "degree", + "dehydrate", + "deity", + "dejected", + "delay", + "delegate", + "delegator", + "delete", + "deletion", + "delicacy", + "delicate", + "delicious", + "delighted", + "delirious", + "delirium", + "deliverer", + "delivery", + "delouse", + "delta", + "deluge", + "delusion", + "deluxe", + "demanding", + "demeaning", + "demeanor", + "demise", + "democracy", + "democrat", + "demote", + "demotion", + "demystify", + "denatured", + "deniable", + "denial", + "denim", + "denote", + "dense", + "density", + "dental", + "dentist", + "denture", + "deny", + "deodorant", + "deodorize", + "departed", + "departure", + "depict", + "deplete", + "depletion", + "deplored", + "deploy", + "deport", + "depose", + "depraved", + "depravity", + "deprecate", + "depress", + "deprive", + "depth", + "deputize", + "deputy", + "derail", + "deranged", + "derby", + "derived", + "desecrate", + "deserve", + "deserving", + "designate", + "designed", + "designer", + "designing", + "deskbound", + "desktop", + "deskwork", + "desolate", + "despair", + "despise", + "despite", + "destiny", + "destitute", + "destruct", + "detached", + "detail", + "detection", + "detective", + "detector", + "detention", + "detergent", + "detest", + "detonate", + "detonator", + "detoxify", + "detract", + "deuce", + "devalue", + "deviancy", + "deviant", + "deviate", + "deviation", + "deviator", + "device", + "devious", + "devotedly", + "devotee", + "devotion", + "devourer", + "devouring", + "devoutly", + "dexterity", + "dexterous", + "diabetes", + "diabetic", + "diabolic", + "diagnoses", + "diagnosis", + "diagram", + "dial", + "diameter", + "diaper", + "diaphragm", + "diary", + "dice", + "dicing", + "dictate", + "dictation", + "dictator", + "difficult", + "diffused", + "diffuser", + "diffusion", + "diffusive", + "dig", + "dilation", + "diligence", + "diligent", + "dill", + "dilute", + "dime", + "diminish", + "dimly", + "dimmed", + "dimmer", + "dimness", + "dimple", + "diner", + "dingbat", + "dinghy", + "dinginess", + "dingo", + "dingy", + "dining", + "dinner", + "diocese", + "dioxide", + "diploma", + "dipped", + "dipper", + "dipping", + "directed", + "direction", + "directive", + "directly", + "directory", + "direness", + "dirtiness", + "disabled", + "disagree", + "disallow", + "disarm", + "disarray", + "disaster", + "disband", + "disbelief", + "disburse", + "discard", + "discern", + "discharge", + "disclose", + "discolor", + "discount", + "discourse", + "discover", + "discuss", + "disdain", + "disengage", + "disfigure", + "disgrace", + "dish", + "disinfect", + "disjoin", + "disk", + "dislike", + "disliking", + "dislocate", + "dislodge", + "disloyal", + "dismantle", + "dismay", + "dismiss", + "dismount", + "disobey", + "disorder", + "disown", + "disparate", + "disparity", + "dispatch", + "dispense", + "dispersal", + "dispersed", + "disperser", + "displace", + "display", + "displease", + "disposal", + "dispose", + "disprove", + "dispute", + "disregard", + "disrupt", + "dissuade", + "distance", + "distant", + "distaste", + "distill", + "distinct", + "distort", + "distract", + "distress", + "district", + "distrust", + "ditch", + "ditto", + "ditzy", + "dividable", + "divided", + "dividend", + "dividers", + "dividing", + "divinely", + "diving", + "divinity", + "divisible", + "divisibly", + "division", + "divisive", + "divorcee", + "dizziness", + "dizzy", + "doable", + "docile", + "dock", + "doctrine", + "document", + "dodge", + "dodgy", + "doily", + "doing", + "dole", + "dollar", + "dollhouse", + "dollop", + "dolly", + "dolphin", + "domain", + "domelike", + "domestic", + "dominion", + "dominoes", + "donated", + "donation", + "donator", + "donor", + "donut", + "doodle", + "doorbell", + "doorframe", + "doorknob", + "doorman", + "doormat", + "doornail", + "doorpost", + "doorstep", + "doorstop", + "doorway", + "doozy", + "dork", + "dormitory", + "dorsal", + "dosage", + "dose", + "dotted", + "doubling", + "douche", + "dove", + "down", + "dowry", + "doze", + "drab", + "dragging", + "dragonfly", + "dragonish", + "dragster", + "drainable", + "drainage", + "drained", + "drainer", + "drainpipe", + "dramatic", + "dramatize", + "drank", + "drapery", + "drastic", + "draw", + "dreaded", + "dreadful", + "dreadlock", + "dreamboat", + "dreamily", + "dreamland", + "dreamless", + "dreamlike", + "dreamt", + "dreamy", + "drearily", + "dreary", + "drench", + "dress", + "drew", + "dribble", + "dried", + "drier", + "drift", + "driller", + "drilling", + "drinkable", + "drinking", + "dripping", + "drippy", + "drivable", + "driven", + "driver", + "driveway", + "driving", + "drizzle", + "drizzly", + "drone", + "drool", + "droop", + "drop-down", + "dropbox", + "dropkick", + "droplet", + "dropout", + "dropper", + "drove", + "drown", + "drowsily", + "drudge", + "drum", + "dry", + "dubbed", + "dubiously", + "duchess", + "duckbill", + "ducking", + "duckling", + "ducktail", + "ducky", + "duct", + "dude", + "duffel", + "dugout", + "duh", + "duke", + "duller", + "dullness", + "duly", + "dumping", + "dumpling", + "dumpster", + "duo", + "dupe", + "duplex", + "duplicate", + "duplicity", + "durable", + "durably", + "duration", + "duress", + "during", + "dusk", + "dust", + "dutiful", + "duty", + "duvet", + "dwarf", + "dweeb", + "dwelled", + "dweller", + "dwelling", + "dwindle", + "dwindling", + "dynamic", + "dynamite", + "dynasty", + "dyslexia", + "dyslexic", + "each", + "eagle", + "earache", + "eardrum", + "earflap", + "earful", + "earlobe", + "early", + "earmark", + "earmuff", + "earphone", + "earpiece", + "earplugs", + "earring", + "earshot", + "earthen", + "earthlike", + "earthling", + "earthly", + "earthworm", + "earthy", + "earwig", + "easeful", + "easel", + "easiest", + "easily", + "easiness", + "easing", + "eastbound", + "eastcoast", + "easter", + "eastward", + "eatable", + "eaten", + "eatery", + "eating", + "eats", + "ebay", + "ebony", + "ebook", + "ecard", + "eccentric", + "echo", + "eclair", + "eclipse", + "ecologist", + "ecology", + "economic", + "economist", + "economy", + "ecosphere", + "ecosystem", + "edge", + "edginess", + "edging", + "edgy", + "edition", + "editor", + "educated", + "education", + "educator", + "eel", + "effective", + "effects", + "efficient", + "effort", + "eggbeater", + "egging", + "eggnog", + "eggplant", + "eggshell", + "egomaniac", + "egotism", + "egotistic", + "either", + "eject", + "elaborate", + "elastic", + "elated", + "elbow", + "eldercare", + "elderly", + "eldest", + "electable", + "election", + "elective", + "elephant", + "elevate", + "elevating", + "elevation", + "elevator", + "eleven", + "elf", + "eligible", + "eligibly", + "eliminate", + "elite", + "elitism", + "elixir", + "elk", + "ellipse", + "elliptic", + "elm", + "elongated", + "elope", + "eloquence", + "eloquent", + "elsewhere", + "elude", + "elusive", + "elves", + "email", + "embargo", + "embark", + "embassy", + "embattled", + "embellish", + "ember", + "embezzle", + "emblaze", + "emblem", + "embody", + "embolism", + "emboss", + "embroider", + "emcee", + "emerald", + "emergency", + "emission", + "emit", + "emote", + "emoticon", + "emotion", + "empathic", + "empathy", + "emperor", + "emphases", + "emphasis", + "emphasize", + "emphatic", + "empirical", + "employed", + "employee", + "employer", + "emporium", + "empower", + "emptier", + "emptiness", + "empty", + "emu", + "enable", + "enactment", + "enamel", + "enchanted", + "enchilada", + "encircle", + "enclose", + "enclosure", + "encode", + "encore", + "encounter", + "encourage", + "encroach", + "encrust", + "encrypt", + "endanger", + "endeared", + "endearing", + "ended", + "ending", + "endless", + "endnote", + "endocrine", + "endorphin", + "endorse", + "endowment", + "endpoint", + "endurable", + "endurance", + "enduring", + "energetic", + "energize", + "energy", + "enforced", + "enforcer", + "engaged", + "engaging", + "engine", + "engorge", + "engraved", + "engraver", + "engraving", + "engross", + "engulf", + "enhance", + "enigmatic", + "enjoyable", + "enjoyably", + "enjoyer", + "enjoying", + "enjoyment", + "enlarged", + "enlarging", + "enlighten", + "enlisted", + "enquirer", + "enrage", + "enrich", + "enroll", + "enslave", + "ensnare", + "ensure", + "entail", + "entangled", + "entering", + "entertain", + "enticing", + "entire", + "entitle", + "entity", + "entomb", + "entourage", + "entrap", + "entree", + "entrench", + "entrust", + "entryway", + "entwine", + "enunciate", + "envelope", + "enviable", + "enviably", + "envious", + "envision", + "envoy", + "envy", + "enzyme", + "epic", + "epidemic", + "epidermal", + "epidermis", + "epidural", + "epilepsy", + "epileptic", + "epilogue", + "epiphany", + "episode", + "equal", + "equate", + "equation", + "equator", + "equinox", + "equipment", + "equity", + "equivocal", + "eradicate", + "erasable", + "erased", + "eraser", + "erasure", + "ergonomic", + "errand", + "errant", + "erratic", + "error", + "erupt", + "escalate", + "escalator", + "escapable", + "escapade", + "escapist", + "escargot", + "eskimo", + "esophagus", + "espionage", + "espresso", + "esquire", + "essay", + "essence", + "essential", + "establish", + "estate", + "esteemed", + "estimate", + "estimator", + "estranged", + "estrogen", + "etching", + "eternal", + "eternity", + "ethanol", + "ether", + "ethically", + "ethics", + "euphemism", + "evacuate", + "evacuee", + "evade", + "evaluate", + "evaluator", + "evaporate", + "evasion", + "evasive", + "even", + "everglade", + "evergreen", + "everybody", + "everyday", + "everyone", + "evict", + "evidence", + "evident", + "evil", + "evoke", + "evolution", + "evolve", + "exact", + "exalted", + "example", + "excavate", + "excavator", + "exceeding", + "exception", + "excess", + "exchange", + "excitable", + "exciting", + "exclaim", + "exclude", + "excluding", + "exclusion", + "exclusive", + "excretion", + "excretory", + "excursion", + "excusable", + "excusably", + "excuse", + "exemplary", + "exemplify", + "exemption", + "exerciser", + "exert", + "exes", + "exfoliate", + "exhale", + "exhaust", + "exhume", + "exile", + "existing", + "exit", + "exodus", + "exonerate", + "exorcism", + "exorcist", + "expand", + "expanse", + "expansion", + "expansive", + "expectant", + "expedited", + "expediter", + "expel", + "expend", + "expenses", + "expensive", + "expert", + "expire", + "expiring", + "explain", + "expletive", + "explicit", + "explode", + "exploit", + "explore", + "exploring", + "exponent", + "exporter", + "exposable", + "expose", + "exposure", + "express", + "expulsion", + "exquisite", + "extended", + "extending", + "extent", + "extenuate", + "exterior", + "external", + "extinct", + "extortion", + "extradite", + "extras", + "extrovert", + "extrude", + "extruding", + "exuberant", + "fable", + "fabric", + "fabulous", + "facebook", + "facecloth", + "facedown", + "faceless", + "facelift", + "faceplate", + "faceted", + "facial", + "facility", + "facing", + "facsimile", + "faction", + "factoid", + "factor", + "factsheet", + "factual", + "faculty", + "fade", + "fading", + "failing", + "falcon", + "fall", + "false", + "falsify", + "fame", + "familiar", + "family", + "famine", + "famished", + "fanatic", + "fancied", + "fanciness", + "fancy", + "fanfare", + "fang", + "fanning", + "fantasize", + "fantastic", + "fantasy", + "fascism", + "fastball", + "faster", + "fasting", + "fastness", + "faucet", + "favorable", + "favorably", + "favored", + "favoring", + "favorite", + "fax", + "feast", + "federal", + "fedora", + "feeble", + "feed", + "feel", + "feisty", + "feline", + "felt-tip", + "feminine", + "feminism", + "feminist", + "feminize", + "femur", + "fence", + "fencing", + "fender", + "ferment", + "fernlike", + "ferocious", + "ferocity", + "ferret", + "ferris", + "ferry", + "fervor", + "fester", + "festival", + "festive", + "festivity", + "fetal", + "fetch", + "fever", + "fiber", + "fiction", + "fiddle", + "fiddling", + "fidelity", + "fidgeting", + "fidgety", + "fifteen", + "fifth", + "fiftieth", + "fifty", + "figment", + "figure", + "figurine", + "filing", + "filled", + "filler", + "filling", + "film", + "filter", + "filth", + "filtrate", + "finale", + "finalist", + "finalize", + "finally", + "finance", + "financial", + "finch", + "fineness", + "finer", + "finicky", + "finished", + "finisher", + "finishing", + "finite", + "finless", + "finlike", + "fiscally", + "fit", + "five", + "flaccid", + "flagman", + "flagpole", + "flagship", + "flagstick", + "flagstone", + "flail", + "flakily", + "flaky", + "flame", + "flammable", + "flanked", + "flanking", + "flannels", + "flap", + "flaring", + "flashback", + "flashbulb", + "flashcard", + "flashily", + "flashing", + "flashy", + "flask", + "flatbed", + "flatfoot", + "flatly", + "flatness", + "flatten", + "flattered", + "flatterer", + "flattery", + "flattop", + "flatware", + "flatworm", + "flavored", + "flavorful", + "flavoring", + "flaxseed", + "fled", + "fleshed", + "fleshy", + "flick", + "flier", + "flight", + "flinch", + "fling", + "flint", + "flip", + "flirt", + "float", + "flock", + "flogging", + "flop", + "floral", + "florist", + "floss", + "flounder", + "flyable", + "flyaway", + "flyer", + "flying", + "flyover", + "flypaper", + "foam", + "foe", + "fog", + "foil", + "folic", + "folk", + "follicle", + "follow", + "fondling", + "fondly", + "fondness", + "fondue", + "font", + "food", + "fool", + "footage", + "football", + "footbath", + "footboard", + "footer", + "footgear", + "foothill", + "foothold", + "footing", + "footless", + "footman", + "footnote", + "footpad", + "footpath", + "footprint", + "footrest", + "footsie", + "footsore", + "footwear", + "footwork", + "fossil", + "foster", + "founder", + "founding", + "fountain", + "fox", + "foyer", + "fraction", + "fracture", + "fragile", + "fragility", + "fragment", + "fragrance", + "fragrant", + "frail", + "frame", + "framing", + "frantic", + "fraternal", + "frayed", + "fraying", + "frays", + "freckled", + "freckles", + "freebase", + "freebee", + "freebie", + "freedom", + "freefall", + "freehand", + "freeing", + "freeload", + "freely", + "freemason", + "freeness", + "freestyle", + "freeware", + "freeway", + "freewill", + "freezable", + "freezing", + "freight", + "french", + "frenzied", + "frenzy", + "frequency", + "frequent", + "fresh", + "fretful", + "fretted", + "friction", + "friday", + "fridge", + "fried", + "friend", + "frighten", + "frightful", + "frigidity", + "frigidly", + "frill", + "fringe", + "frisbee", + "frisk", + "fritter", + "frivolous", + "frolic", + "from", + "front", + "frostbite", + "frosted", + "frostily", + "frosting", + "frostlike", + "frosty", + "froth", + "frown", + "frozen", + "fructose", + "frugality", + "frugally", + "fruit", + "frustrate", + "frying", + "gab", + "gaffe", + "gag", + "gainfully", + "gaining", + "gains", + "gala", + "gallantly", + "galleria", + "gallery", + "galley", + "gallon", + "gallows", + "gallstone", + "galore", + "galvanize", + "gambling", + "game", + "gaming", + "gamma", + "gander", + "gangly", + "gangrene", + "gangway", + "gap", + "garage", + "garbage", + "garden", + "gargle", + "garland", + "garlic", + "garment", + "garnet", + "garnish", + "garter", + "gas", + "gatherer", + "gathering", + "gating", + "gauging", + "gauntlet", + "gauze", + "gave", + "gawk", + "gazing", + "gear", + "gecko", + "geek", + "geiger", + "gem", + "gender", + "generic", + "generous", + "genetics", + "genre", + "gentile", + "gentleman", + "gently", + "gents", + "geography", + "geologic", + "geologist", + "geology", + "geometric", + "geometry", + "geranium", + "gerbil", + "geriatric", + "germicide", + "germinate", + "germless", + "germproof", + "gestate", + "gestation", + "gesture", + "getaway", + "getting", + "getup", + "giant", + "gibberish", + "giblet", + "giddily", + "giddiness", + "giddy", + "gift", + "gigabyte", + "gigahertz", + "gigantic", + "giggle", + "giggling", + "giggly", + "gigolo", + "gilled", + "gills", + "gimmick", + "girdle", + "giveaway", + "given", + "giver", + "giving", + "gizmo", + "gizzard", + "glacial", + "glacier", + "glade", + "gladiator", + "gladly", + "glamorous", + "glamour", + "glance", + "glancing", + "glandular", + "glare", + "glaring", + "glass", + "glaucoma", + "glazing", + "gleaming", + "gleeful", + "glider", + "gliding", + "glimmer", + "glimpse", + "glisten", + "glitch", + "glitter", + "glitzy", + "gloater", + "gloating", + "gloomily", + "gloomy", + "glorified", + "glorifier", + "glorify", + "glorious", + "glory", + "gloss", + "glove", + "glowing", + "glowworm", + "glucose", + "glue", + "gluten", + "glutinous", + "glutton", + "gnarly", + "gnat", + "goal", + "goatskin", + "goes", + "goggles", + "going", + "goldfish", + "goldmine", + "goldsmith", + "golf", + "goliath", + "gonad", + "gondola", + "gone", + "gong", + "good", + "gooey", + "goofball", + "goofiness", + "goofy", + "google", + "goon", + "gopher", + "gore", + "gorged", + "gorgeous", + "gory", + "gosling", + "gossip", + "gothic", + "gotten", + "gout", + "gown", + "grab", + "graceful", + "graceless", + "gracious", + "gradation", + "graded", + "grader", + "gradient", + "grading", + "gradually", + "graduate", + "graffiti", + "grafted", + "grafting", + "grain", + "granddad", + "grandkid", + "grandly", + "grandma", + "grandpa", + "grandson", + "granite", + "granny", + "granola", + "grant", + "granular", + "grape", + "graph", + "grapple", + "grappling", + "grasp", + "grass", + "gratified", + "gratify", + "grating", + "gratitude", + "gratuity", + "gravel", + "graveness", + "graves", + "graveyard", + "gravitate", + "gravity", + "gravy", + "gray", + "grazing", + "greasily", + "greedily", + "greedless", + "greedy", + "green", + "greeter", + "greeting", + "grew", + "greyhound", + "grid", + "grief", + "grievance", + "grieving", + "grievous", + "grill", + "grimace", + "grimacing", + "grime", + "griminess", + "grimy", + "grinch", + "grinning", + "grip", + "gristle", + "grit", + "groggily", + "groggy", + "groin", + "groom", + "groove", + "grooving", + "groovy", + "grope", + "ground", + "grouped", + "grout", + "grove", + "grower", + "growing", + "growl", + "grub", + "grudge", + "grudging", + "grueling", + "gruffly", + "grumble", + "grumbling", + "grumbly", + "grumpily", + "grunge", + "grunt", + "guacamole", + "guidable", + "guidance", + "guide", + "guiding", + "guileless", + "guise", + "gulf", + "gullible", + "gully", + "gulp", + "gumball", + "gumdrop", + "gumminess", + "gumming", + "gummy", + "gurgle", + "gurgling", + "guru", + "gush", + "gusto", + "gusty", + "gutless", + "guts", + "gutter", + "guy", + "guzzler", + "gyration", + "habitable", + "habitant", + "habitat", + "habitual", + "hacked", + "hacker", + "hacking", + "hacksaw", + "had", + "haggler", + "haiku", + "half", + "halogen", + "halt", + "halved", + "halves", + "hamburger", + "hamlet", + "hammock", + "hamper", + "hamster", + "hamstring", + "handbag", + "handball", + "handbook", + "handbrake", + "handcart", + "handclap", + "handclasp", + "handcraft", + "handcuff", + "handed", + "handful", + "handgrip", + "handgun", + "handheld", + "handiness", + "handiwork", + "handlebar", + "handled", + "handler", + "handling", + "handmade", + "handoff", + "handpick", + "handprint", + "handrail", + "handsaw", + "handset", + "handsfree", + "handshake", + "handstand", + "handwash", + "handwork", + "handwoven", + "handwrite", + "handyman", + "hangnail", + "hangout", + "hangover", + "hangup", + "hankering", + "hankie", + "hanky", + "haphazard", + "happening", + "happier", + "happiest", + "happily", + "happiness", + "happy", + "harbor", + "hardcopy", + "hardcore", + "hardcover", + "harddisk", + "hardened", + "hardener", + "hardening", + "hardhat", + "hardhead", + "hardiness", + "hardly", + "hardness", + "hardship", + "hardware", + "hardwired", + "hardwood", + "hardy", + "harmful", + "harmless", + "harmonica", + "harmonics", + "harmonize", + "harmony", + "harness", + "harpist", + "harsh", + "harvest", + "hash", + "hassle", + "haste", + "hastily", + "hastiness", + "hasty", + "hatbox", + "hatchback", + "hatchery", + "hatchet", + "hatching", + "hatchling", + "hate", + "hatless", + "hatred", + "haunt", + "haven", + "hazard", + "hazelnut", + "hazily", + "haziness", + "hazing", + "hazy", + "headache", + "headband", + "headboard", + "headcount", + "headdress", + "headed", + "header", + "headfirst", + "headgear", + "heading", + "headlamp", + "headless", + "headlock", + "headphone", + "headpiece", + "headrest", + "headroom", + "headscarf", + "headset", + "headsman", + "headstand", + "headstone", + "headway", + "headwear", + "heap", + "heat", + "heave", + "heavily", + "heaviness", + "heaving", + "hedge", + "hedging", + "heftiness", + "hefty", + "helium", + "helmet", + "helper", + "helpful", + "helping", + "helpless", + "helpline", + "hemlock", + "hemstitch", + "hence", + "henchman", + "henna", + "herald", + "herbal", + "herbicide", + "herbs", + "heritage", + "hermit", + "heroics", + "heroism", + "herring", + "herself", + "hertz", + "hesitancy", + "hesitant", + "hesitate", + "hexagon", + "hexagram", + "hubcap", + "huddle", + "huddling", + "huff", + "hug", + "hula", + "hulk", + "hull", + "human", + "humble", + "humbling", + "humbly", + "humid", + "humiliate", + "humility", + "humming", + "hummus", + "humongous", + "humorist", + "humorless", + "humorous", + "humpback", + "humped", + "humvee", + "hunchback", + "hundredth", + "hunger", + "hungrily", + "hungry", + "hunk", + "hunter", + "hunting", + "huntress", + "huntsman", + "hurdle", + "hurled", + "hurler", + "hurling", + "hurray", + "hurricane", + "hurried", + "hurry", + "hurt", + "husband", + "hush", + "husked", + "huskiness", + "hut", + "hybrid", + "hydrant", + "hydrated", + "hydration", + "hydrogen", + "hydroxide", + "hyperlink", + "hypertext", + "hyphen", + "hypnoses", + "hypnosis", + "hypnotic", + "hypnotism", + "hypnotist", + "hypnotize", + "hypocrisy", + "hypocrite", + "ibuprofen", + "ice", + "iciness", + "icing", + "icky", + "icon", + "icy", + "idealism", + "idealist", + "idealize", + "ideally", + "idealness", + "identical", + "identify", + "identity", + "ideology", + "idiocy", + "idiom", + "idly", + "igloo", + "ignition", + "ignore", + "iguana", + "illicitly", + "illusion", + "illusive", + "image", + "imaginary", + "imagines", + "imaging", + "imbecile", + "imitate", + "imitation", + "immature", + "immerse", + "immersion", + "imminent", + "immobile", + "immodest", + "immorally", + "immortal", + "immovable", + "immovably", + "immunity", + "immunize", + "impaired", + "impale", + "impart", + "impatient", + "impeach", + "impeding", + "impending", + "imperfect", + "imperial", + "impish", + "implant", + "implement", + "implicate", + "implicit", + "implode", + "implosion", + "implosive", + "imply", + "impolite", + "important", + "importer", + "impose", + "imposing", + "impotence", + "impotency", + "impotent", + "impound", + "imprecise", + "imprint", + "imprison", + "impromptu", + "improper", + "improve", + "improving", + "improvise", + "imprudent", + "impulse", + "impulsive", + "impure", + "impurity", + "iodine", + "iodize", + "ion", + "ipad", + "iphone", + "ipod", + "irate", + "irk", + "iron", + "irregular", + "irrigate", + "irritable", + "irritably", + "irritant", + "irritate", + "islamic", + "islamist", + "isolated", + "isolating", + "isolation", + "isotope", + "issue", + "issuing", + "italicize", + "italics", + "item", + "itinerary", + "itunes", + "ivory", + "ivy", + "jab", + "jackal", + "jacket", + "jackknife", + "jackpot", + "jailbird", + "jailbreak", + "jailer", + "jailhouse", + "jalapeno", + "jam", + "janitor", + "january", + "jargon", + "jarring", + "jasmine", + "jaundice", + "jaunt", + "java", + "jawed", + "jawless", + "jawline", + "jaws", + "jaybird", + "jaywalker", + "jazz", + "jeep", + "jeeringly", + "jellied", + "jelly", + "jersey", + "jester", + "jet", + "jiffy", + "jigsaw", + "jimmy", + "jingle", + "jingling", + "jinx", + "jitters", + "jittery", + "job", + "jockey", + "jockstrap", + "jogger", + "jogging", + "john", + "joining", + "jokester", + "jokingly", + "jolliness", + "jolly", + "jolt", + "jot", + "jovial", + "joyfully", + "joylessly", + "joyous", + "joyride", + "joystick", + "jubilance", + "jubilant", + "judge", + "judgingly", + "judicial", + "judiciary", + "judo", + "juggle", + "juggling", + "jugular", + "juice", + "juiciness", + "juicy", + "jujitsu", + "jukebox", + "july", + "jumble", + "jumbo", + "jump", + "junction", + "juncture", + "june", + "junior", + "juniper", + "junkie", + "junkman", + "junkyard", + "jurist", + "juror", + "jury", + "justice", + "justifier", + "justify", + "justly", + "justness", + "juvenile", + "kabob", + "kangaroo", + "karaoke", + "karate", + "karma", + "kebab", + "keenly", + "keenness", + "keep", + "keg", + "kelp", + "kennel", + "kept", + "kerchief", + "kerosene", + "kettle", + "kick", + "kiln", + "kilobyte", + "kilogram", + "kilometer", + "kilowatt", + "kilt", + "kimono", + "kindle", + "kindling", + "kindly", + "kindness", + "kindred", + "kinetic", + "kinfolk", + "king", + "kinship", + "kinsman", + "kinswoman", + "kissable", + "kisser", + "kissing", + "kitchen", + "kite", + "kitten", + "kitty", + "kiwi", + "kleenex", + "knapsack", + "knee", + "knelt", + "knickers", + "knoll", + "koala", + "kooky", + "kosher", + "krypton", + "kudos", + "kung", + "labored", + "laborer", + "laboring", + "laborious", + "labrador", + "ladder", + "ladies", + "ladle", + "ladybug", + "ladylike", + "lagged", + "lagging", + "lagoon", + "lair", + "lake", + "lance", + "landed", + "landfall", + "landfill", + "landing", + "landlady", + "landless", + "landline", + "landlord", + "landmark", + "landmass", + "landmine", + "landowner", + "landscape", + "landside", + "landslide", + "language", + "lankiness", + "lanky", + "lantern", + "lapdog", + "lapel", + "lapped", + "lapping", + "laptop", + "lard", + "large", + "lark", + "lash", + "lasso", + "last", + "latch", + "late", + "lather", + "latitude", + "latrine", + "latter", + "latticed", + "launch", + "launder", + "laundry", + "laurel", + "lavender", + "lavish", + "laxative", + "lazily", + "laziness", + "lazy", + "lecturer", + "left", + "legacy", + "legal", + "legend", + "legged", + "leggings", + "legible", + "legibly", + "legislate", + "lego", + "legroom", + "legume", + "legwarmer", + "legwork", + "lemon", + "lend", + "length", + "lens", + "lent", + "leotard", + "lesser", + "letdown", + "lethargic", + "lethargy", + "letter", + "lettuce", + "level", + "leverage", + "levers", + "levitate", + "levitator", + "liability", + "liable", + "liberty", + "librarian", + "library", + "licking", + "licorice", + "lid", + "life", + "lifter", + "lifting", + "liftoff", + "ligament", + "likely", + "likeness", + "likewise", + "liking", + "lilac", + "lilly", + "lily", + "limb", + "limeade", + "limelight", + "limes", + "limit", + "limping", + "limpness", + "line", + "lingo", + "linguini", + "linguist", + "lining", + "linked", + "linoleum", + "linseed", + "lint", + "lion", + "lip", + "liquefy", + "liqueur", + "liquid", + "lisp", + "list", + "litigate", + "litigator", + "litmus", + "litter", + "little", + "livable", + "lived", + "lively", + "liver", + "livestock", + "lividly", + "living", + "lizard", + "lubricant", + "lubricate", + "lucid", + "luckily", + "luckiness", + "luckless", + "lucrative", + "ludicrous", + "lugged", + "lukewarm", + "lullaby", + "lumber", + "luminance", + "luminous", + "lumpiness", + "lumping", + "lumpish", + "lunacy", + "lunar", + "lunchbox", + "luncheon", + "lunchroom", + "lunchtime", + "lung", + "lurch", + "lure", + "luridness", + "lurk", + "lushly", + "lushness", + "luster", + "lustfully", + "lustily", + "lustiness", + "lustrous", + "lusty", + "luxurious", + "luxury", + "lying", + "lyrically", + "lyricism", + "lyricist", + "lyrics", + "macarena", + "macaroni", + "macaw", + "mace", + "machine", + "machinist", + "magazine", + "magenta", + "maggot", + "magical", + "magician", + "magma", + "magnesium", + "magnetic", + "magnetism", + "magnetize", + "magnifier", + "magnify", + "magnitude", + "magnolia", + "mahogany", + "maimed", + "majestic", + "majesty", + "majorette", + "majority", + "makeover", + "maker", + "makeshift", + "making", + "malformed", + "malt", + "mama", + "mammal", + "mammary", + "mammogram", + "manager", + "managing", + "manatee", + "mandarin", + "mandate", + "mandatory", + "mandolin", + "manger", + "mangle", + "mango", + "mangy", + "manhandle", + "manhole", + "manhood", + "manhunt", + "manicotti", + "manicure", + "manifesto", + "manila", + "mankind", + "manlike", + "manliness", + "manly", + "manmade", + "manned", + "mannish", + "manor", + "manpower", + "mantis", + "mantra", + "manual", + "many", + "map", + "marathon", + "marauding", + "marbled", + "marbles", + "marbling", + "march", + "mardi", + "margarine", + "margarita", + "margin", + "marigold", + "marina", + "marine", + "marital", + "maritime", + "marlin", + "marmalade", + "maroon", + "married", + "marrow", + "marry", + "marshland", + "marshy", + "marsupial", + "marvelous", + "marxism", + "mascot", + "masculine", + "mashed", + "mashing", + "massager", + "masses", + "massive", + "mastiff", + "matador", + "matchbook", + "matchbox", + "matcher", + "matching", + "matchless", + "material", + "maternal", + "maternity", + "math", + "mating", + "matriarch", + "matrimony", + "matrix", + "matron", + "matted", + "matter", + "maturely", + "maturing", + "maturity", + "mauve", + "maverick", + "maximize", + "maximum", + "maybe", + "mayday", + "mayflower", + "moaner", + "moaning", + "mobile", + "mobility", + "mobilize", + "mobster", + "mocha", + "mocker", + "mockup", + "modified", + "modify", + "modular", + "modulator", + "module", + "moisten", + "moistness", + "moisture", + "molar", + "molasses", + "mold", + "molecular", + "molecule", + "molehill", + "mollusk", + "mom", + "monastery", + "monday", + "monetary", + "monetize", + "moneybags", + "moneyless", + "moneywise", + "mongoose", + "mongrel", + "monitor", + "monkhood", + "monogamy", + "monogram", + "monologue", + "monopoly", + "monorail", + "monotone", + "monotype", + "monoxide", + "monsieur", + "monsoon", + "monstrous", + "monthly", + "monument", + "moocher", + "moodiness", + "moody", + "mooing", + "moonbeam", + "mooned", + "moonlight", + "moonlike", + "moonlit", + "moonrise", + "moonscape", + "moonshine", + "moonstone", + "moonwalk", + "mop", + "morale", + "morality", + "morally", + "morbidity", + "morbidly", + "morphine", + "morphing", + "morse", + "mortality", + "mortally", + "mortician", + "mortified", + "mortify", + "mortuary", + "mosaic", + "mossy", + "most", + "mothball", + "mothproof", + "motion", + "motivate", + "motivator", + "motive", + "motocross", + "motor", + "motto", + "mountable", + "mountain", + "mounted", + "mounting", + "mourner", + "mournful", + "mouse", + "mousiness", + "moustache", + "mousy", + "mouth", + "movable", + "move", + "movie", + "moving", + "mower", + "mowing", + "much", + "muck", + "mud", + "mug", + "mulberry", + "mulch", + "mule", + "mulled", + "mullets", + "multiple", + "multiply", + "multitask", + "multitude", + "mumble", + "mumbling", + "mumbo", + "mummified", + "mummify", + "mummy", + "mumps", + "munchkin", + "mundane", + "municipal", + "muppet", + "mural", + "murkiness", + "murky", + "murmuring", + "muscular", + "museum", + "mushily", + "mushiness", + "mushroom", + "mushy", + "music", + "musket", + "muskiness", + "musky", + "mustang", + "mustard", + "muster", + "mustiness", + "musty", + "mutable", + "mutate", + "mutation", + "mute", + "mutilated", + "mutilator", + "mutiny", + "mutt", + "mutual", + "muzzle", + "myself", + "myspace", + "mystified", + "mystify", + "myth", + "nacho", + "nag", + "nail", + "name", + "naming", + "nanny", + "nanometer", + "nape", + "napkin", + "napped", + "napping", + "nappy", + "narrow", + "nastily", + "nastiness", + "national", + "native", + "nativity", + "natural", + "nature", + "naturist", + "nautical", + "navigate", + "navigator", + "navy", + "nearby", + "nearest", + "nearly", + "nearness", + "neatly", + "neatness", + "nebula", + "nebulizer", + "nectar", + "negate", + "negation", + "negative", + "neglector", + "negligee", + "negligent", + "negotiate", + "nemeses", + "nemesis", + "neon", + "nephew", + "nerd", + "nervous", + "nervy", + "nest", + "net", + "neurology", + "neuron", + "neurosis", + "neurotic", + "neuter", + "neutron", + "never", + "next", + "nibble", + "nickname", + "nicotine", + "niece", + "nifty", + "nimble", + "nimbly", + "nineteen", + "ninetieth", + "ninja", + "nintendo", + "ninth", + "nuclear", + "nuclei", + "nucleus", + "nugget", + "nullify", + "number", + "numbing", + "numbly", + "numbness", + "numeral", + "numerate", + "numerator", + "numeric", + "numerous", + "nuptials", + "nursery", + "nursing", + "nurture", + "nutcase", + "nutlike", + "nutmeg", + "nutrient", + "nutshell", + "nuttiness", + "nutty", + "nuzzle", + "nylon", + "oaf", + "oak", + "oasis", + "oat", + "obedience", + "obedient", + "obituary", + "object", + "obligate", + "obliged", + "oblivion", + "oblivious", + "oblong", + "obnoxious", + "oboe", + "obscure", + "obscurity", + "observant", + "observer", + "observing", + "obsessed", + "obsession", + "obsessive", + "obsolete", + "obstacle", + "obstinate", + "obstruct", + "obtain", + "obtrusive", + "obtuse", + "obvious", + "occultist", + "occupancy", + "occupant", + "occupier", + "occupy", + "ocean", + "ocelot", + "octagon", + "octane", + "october", + "octopus", + "ogle", + "oil", + "oink", + "ointment", + "okay", + "old", + "olive", + "olympics", + "omega", + "omen", + "ominous", + "omission", + "omit", + "omnivore", + "onboard", + "oncoming", + "ongoing", + "onion", + "online", + "onlooker", + "only", + "onscreen", + "onset", + "onshore", + "onslaught", + "onstage", + "onto", + "onward", + "onyx", + "oops", + "ooze", + "oozy", + "opacity", + "opal", + "open", + "operable", + "operate", + "operating", + "operation", + "operative", + "operator", + "opium", + "opossum", + "opponent", + "oppose", + "opposing", + "opposite", + "oppressed", + "oppressor", + "opt", + "opulently", + "osmosis", + "other", + "otter", + "ouch", + "ought", + "ounce", + "outage", + "outback", + "outbid", + "outboard", + "outbound", + "outbreak", + "outburst", + "outcast", + "outclass", + "outcome", + "outdated", + "outdoors", + "outer", + "outfield", + "outfit", + "outflank", + "outgoing", + "outgrow", + "outhouse", + "outing", + "outlast", + "outlet", + "outline", + "outlook", + "outlying", + "outmatch", + "outmost", + "outnumber", + "outplayed", + "outpost", + "outpour", + "output", + "outrage", + "outrank", + "outreach", + "outright", + "outscore", + "outsell", + "outshine", + "outshoot", + "outsider", + "outskirts", + "outsmart", + "outsource", + "outspoken", + "outtakes", + "outthink", + "outward", + "outweigh", + "outwit", + "oval", + "ovary", + "oven", + "overact", + "overall", + "overarch", + "overbid", + "overbill", + "overbite", + "overblown", + "overboard", + "overbook", + "overbuilt", + "overcast", + "overcoat", + "overcome", + "overcook", + "overcrowd", + "overdraft", + "overdrawn", + "overdress", + "overdrive", + "overdue", + "overeager", + "overeater", + "overexert", + "overfed", + "overfeed", + "overfill", + "overflow", + "overfull", + "overgrown", + "overhand", + "overhang", + "overhaul", + "overhead", + "overhear", + "overheat", + "overhung", + "overjoyed", + "overkill", + "overlabor", + "overlaid", + "overlap", + "overlay", + "overload", + "overlook", + "overlord", + "overlying", + "overnight", + "overpass", + "overpay", + "overplant", + "overplay", + "overpower", + "overprice", + "overrate", + "overreach", + "overreact", + "override", + "overripe", + "overrule", + "overrun", + "overshoot", + "overshot", + "oversight", + "oversized", + "oversleep", + "oversold", + "overspend", + "overstate", + "overstay", + "overstep", + "overstock", + "overstuff", + "oversweet", + "overtake", + "overthrow", + "overtime", + "overtly", + "overtone", + "overture", + "overturn", + "overuse", + "overvalue", + "overview", + "overwrite", + "owl", + "oxford", + "oxidant", + "oxidation", + "oxidize", + "oxidizing", + "oxygen", + "oxymoron", + "oyster", + "ozone", + "paced", + "pacemaker", + "pacific", + "pacifier", + "pacifism", + "pacifist", + "pacify", + "padded", + "padding", + "paddle", + "paddling", + "padlock", + "pagan", + "pager", + "paging", + "pajamas", + "palace", + "palatable", + "palm", + "palpable", + "palpitate", + "paltry", + "pampered", + "pamperer", + "pampers", + "pamphlet", + "panama", + "pancake", + "pancreas", + "panda", + "pandemic", + "pang", + "panhandle", + "panic", + "panning", + "panorama", + "panoramic", + "panther", + "pantomime", + "pantry", + "pants", + "pantyhose", + "paparazzi", + "papaya", + "paper", + "paprika", + "papyrus", + "parabola", + "parachute", + "parade", + "paradox", + "paragraph", + "parakeet", + "paralegal", + "paralyses", + "paralysis", + "paralyze", + "paramedic", + "parameter", + "paramount", + "parasail", + "parasite", + "parasitic", + "parcel", + "parched", + "parchment", + "pardon", + "parish", + "parka", + "parking", + "parkway", + "parlor", + "parmesan", + "parole", + "parrot", + "parsley", + "parsnip", + "partake", + "parted", + "parting", + "partition", + "partly", + "partner", + "partridge", + "party", + "passable", + "passably", + "passage", + "passcode", + "passenger", + "passerby", + "passing", + "passion", + "passive", + "passivism", + "passover", + "passport", + "password", + "pasta", + "pasted", + "pastel", + "pastime", + "pastor", + "pastrami", + "pasture", + "pasty", + "patchwork", + "patchy", + "paternal", + "paternity", + "path", + "patience", + "patient", + "patio", + "patriarch", + "patriot", + "patrol", + "patronage", + "patronize", + "pauper", + "pavement", + "paver", + "pavestone", + "pavilion", + "paving", + "pawing", + "payable", + "payback", + "paycheck", + "payday", + "payee", + "payer", + "paying", + "payment", + "payphone", + "payroll", + "pebble", + "pebbly", + "pecan", + "pectin", + "peculiar", + "peddling", + "pediatric", + "pedicure", + "pedigree", + "pedometer", + "pegboard", + "pelican", + "pellet", + "pelt", + "pelvis", + "penalize", + "penalty", + "pencil", + "pendant", + "pending", + "penholder", + "penknife", + "pennant", + "penniless", + "penny", + "penpal", + "pension", + "pentagon", + "pentagram", + "pep", + "perceive", + "percent", + "perch", + "percolate", + "perennial", + "perfected", + "perfectly", + "perfume", + "periscope", + "perish", + "perjurer", + "perjury", + "perkiness", + "perky", + "perm", + "peroxide", + "perpetual", + "perplexed", + "persecute", + "persevere", + "persuaded", + "persuader", + "pesky", + "peso", + "pessimism", + "pessimist", + "pester", + "pesticide", + "petal", + "petite", + "petition", + "petri", + "petroleum", + "petted", + "petticoat", + "pettiness", + "petty", + "petunia", + "phantom", + "phobia", + "phoenix", + "phonebook", + "phoney", + "phonics", + "phoniness", + "phony", + "phosphate", + "photo", + "phrase", + "phrasing", + "placard", + "placate", + "placidly", + "plank", + "planner", + "plant", + "plasma", + "plaster", + "plastic", + "plated", + "platform", + "plating", + "platinum", + "platonic", + "platter", + "platypus", + "plausible", + "plausibly", + "playable", + "playback", + "player", + "playful", + "playgroup", + "playhouse", + "playing", + "playlist", + "playmaker", + "playmate", + "playoff", + "playpen", + "playroom", + "playset", + "plaything", + "playtime", + "plaza", + "pleading", + "pleat", + "pledge", + "plentiful", + "plenty", + "plethora", + "plexiglas", + "pliable", + "plod", + "plop", + "plot", + "plow", + "ploy", + "pluck", + "plug", + "plunder", + "plunging", + "plural", + "plus", + "plutonium", + "plywood", + "poach", + "pod", + "poem", + "poet", + "pogo", + "pointed", + "pointer", + "pointing", + "pointless", + "pointy", + "poise", + "poison", + "poker", + "poking", + "polar", + "police", + "policy", + "polio", + "polish", + "politely", + "polka", + "polo", + "polyester", + "polygon", + "polygraph", + "polymer", + "poncho", + "pond", + "pony", + "popcorn", + "pope", + "poplar", + "popper", + "poppy", + "popsicle", + "populace", + "popular", + "populate", + "porcupine", + "pork", + "porous", + "porridge", + "portable", + "portal", + "portfolio", + "porthole", + "portion", + "portly", + "portside", + "poser", + "posh", + "posing", + "possible", + "possibly", + "possum", + "postage", + "postal", + "postbox", + "postcard", + "posted", + "poster", + "posting", + "postnasal", + "posture", + "postwar", + "pouch", + "pounce", + "pouncing", + "pound", + "pouring", + "pout", + "powdered", + "powdering", + "powdery", + "power", + "powwow", + "pox", + "praising", + "prance", + "prancing", + "pranker", + "prankish", + "prankster", + "prayer", + "praying", + "preacher", + "preaching", + "preachy", + "preamble", + "precinct", + "precise", + "precision", + "precook", + "precut", + "predator", + "predefine", + "predict", + "preface", + "prefix", + "preflight", + "preformed", + "pregame", + "pregnancy", + "pregnant", + "preheated", + "prelaunch", + "prelaw", + "prelude", + "premiere", + "premises", + "premium", + "prenatal", + "preoccupy", + "preorder", + "prepaid", + "prepay", + "preplan", + "preppy", + "preschool", + "prescribe", + "preseason", + "preset", + "preshow", + "president", + "presoak", + "press", + "presume", + "presuming", + "preteen", + "pretended", + "pretender", + "pretense", + "pretext", + "pretty", + "pretzel", + "prevail", + "prevalent", + "prevent", + "preview", + "previous", + "prewar", + "prewashed", + "prideful", + "pried", + "primal", + "primarily", + "primary", + "primate", + "primer", + "primp", + "princess", + "print", + "prior", + "prism", + "prison", + "prissy", + "pristine", + "privacy", + "private", + "privatize", + "prize", + "proactive", + "probable", + "probably", + "probation", + "probe", + "probing", + "probiotic", + "problem", + "procedure", + "process", + "proclaim", + "procreate", + "procurer", + "prodigal", + "prodigy", + "produce", + "product", + "profane", + "profanity", + "professed", + "professor", + "profile", + "profound", + "profusely", + "progeny", + "prognosis", + "program", + "progress", + "projector", + "prologue", + "prolonged", + "promenade", + "prominent", + "promoter", + "promotion", + "prompter", + "promptly", + "prone", + "prong", + "pronounce", + "pronto", + "proofing", + "proofread", + "proofs", + "propeller", + "properly", + "property", + "proponent", + "proposal", + "propose", + "props", + "prorate", + "protector", + "protegee", + "proton", + "prototype", + "protozoan", + "protract", + "protrude", + "proud", + "provable", + "proved", + "proven", + "provided", + "provider", + "providing", + "province", + "proving", + "provoke", + "provoking", + "provolone", + "prowess", + "prowler", + "prowling", + "proximity", + "proxy", + "prozac", + "prude", + "prudishly", + "prune", + "pruning", + "pry", + "psychic", + "public", + "publisher", + "pucker", + "pueblo", + "pug", + "pull", + "pulmonary", + "pulp", + "pulsate", + "pulse", + "pulverize", + "puma", + "pumice", + "pummel", + "punch", + "punctual", + "punctuate", + "punctured", + "pungent", + "punisher", + "punk", + "pupil", + "puppet", + "puppy", + "purchase", + "pureblood", + "purebred", + "purely", + "pureness", + "purgatory", + "purge", + "purging", + "purifier", + "purify", + "purist", + "puritan", + "purity", + "purple", + "purplish", + "purposely", + "purr", + "purse", + "pursuable", + "pursuant", + "pursuit", + "purveyor", + "pushcart", + "pushchair", + "pusher", + "pushiness", + "pushing", + "pushover", + "pushpin", + "pushup", + "pushy", + "putdown", + "putt", + "puzzle", + "puzzling", + "pyramid", + "pyromania", + "python", + "quack", + "quadrant", + "quail", + "quaintly", + "quake", + "quaking", + "qualified", + "qualifier", + "qualify", + "quality", + "qualm", + "quantum", + "quarrel", + "quarry", + "quartered", + "quarterly", + "quarters", + "quartet", + "quench", + "query", + "quicken", + "quickly", + "quickness", + "quicksand", + "quickstep", + "quiet", + "quill", + "quilt", + "quintet", + "quintuple", + "quirk", + "quit", + "quiver", + "quizzical", + "quotable", + "quotation", + "quote", + "rabid", + "race", + "racing", + "racism", + "rack", + "racoon", + "radar", + "radial", + "radiance", + "radiantly", + "radiated", + "radiation", + "radiator", + "radio", + "radish", + "raffle", + "raft", + "rage", + "ragged", + "raging", + "ragweed", + "raider", + "railcar", + "railing", + "railroad", + "railway", + "raisin", + "rake", + "raking", + "rally", + "ramble", + "rambling", + "ramp", + "ramrod", + "ranch", + "rancidity", + "random", + "ranged", + "ranger", + "ranging", + "ranked", + "ranking", + "ransack", + "ranting", + "rants", + "rare", + "rarity", + "rascal", + "rash", + "rasping", + "ravage", + "raven", + "ravine", + "raving", + "ravioli", + "ravishing", + "reabsorb", + "reach", + "reacquire", + "reaction", + "reactive", + "reactor", + "reaffirm", + "ream", + "reanalyze", + "reappear", + "reapply", + "reappoint", + "reapprove", + "rearrange", + "rearview", + "reason", + "reassign", + "reassure", + "reattach", + "reawake", + "rebalance", + "rebate", + "rebel", + "rebirth", + "reboot", + "reborn", + "rebound", + "rebuff", + "rebuild", + "rebuilt", + "reburial", + "rebuttal", + "recall", + "recant", + "recapture", + "recast", + "recede", + "recent", + "recess", + "recharger", + "recipient", + "recital", + "recite", + "reckless", + "reclaim", + "recliner", + "reclining", + "recluse", + "reclusive", + "recognize", + "recoil", + "recollect", + "recolor", + "reconcile", + "reconfirm", + "reconvene", + "recopy", + "record", + "recount", + "recoup", + "recovery", + "recreate", + "rectal", + "rectangle", + "rectified", + "rectify", + "recycled", + "recycler", + "recycling", + "reemerge", + "reenact", + "reenter", + "reentry", + "reexamine", + "referable", + "referee", + "reference", + "refill", + "refinance", + "refined", + "refinery", + "refining", + "refinish", + "reflected", + "reflector", + "reflex", + "reflux", + "refocus", + "refold", + "reforest", + "reformat", + "reformed", + "reformer", + "reformist", + "refract", + "refrain", + "refreeze", + "refresh", + "refried", + "refueling", + "refund", + "refurbish", + "refurnish", + "refusal", + "refuse", + "refusing", + "refutable", + "refute", + "regain", + "regalia", + "regally", + "reggae", + "regime", + "region", + "register", + "registrar", + "registry", + "regress", + "regretful", + "regroup", + "regular", + "regulate", + "regulator", + "rehab", + "reheat", + "rehire", + "rehydrate", + "reimburse", + "reissue", + "reiterate", + "rejoice", + "rejoicing", + "rejoin", + "rekindle", + "relapse", + "relapsing", + "relatable", + "related", + "relation", + "relative", + "relax", + "relay", + "relearn", + "release", + "relenting", + "reliable", + "reliably", + "reliance", + "reliant", + "relic", + "relieve", + "relieving", + "relight", + "relish", + "relive", + "reload", + "relocate", + "relock", + "reluctant", + "rely", + "remake", + "remark", + "remarry", + "rematch", + "remedial", + "remedy", + "remember", + "reminder", + "remindful", + "remission", + "remix", + "remnant", + "remodeler", + "remold", + "remorse", + "remote", + "removable", + "removal", + "removed", + "remover", + "removing", + "rename", + "renderer", + "rendering", + "rendition", + "renegade", + "renewable", + "renewably", + "renewal", + "renewed", + "renounce", + "renovate", + "renovator", + "rentable", + "rental", + "rented", + "renter", + "reoccupy", + "reoccur", + "reopen", + "reorder", + "repackage", + "repacking", + "repaint", + "repair", + "repave", + "repaying", + "repayment", + "repeal", + "repeated", + "repeater", + "repent", + "rephrase", + "replace", + "replay", + "replica", + "reply", + "reporter", + "repose", + "repossess", + "repost", + "repressed", + "reprimand", + "reprint", + "reprise", + "reproach", + "reprocess", + "reproduce", + "reprogram", + "reps", + "reptile", + "reptilian", + "repugnant", + "repulsion", + "repulsive", + "repurpose", + "reputable", + "reputably", + "request", + "require", + "requisite", + "reroute", + "rerun", + "resale", + "resample", + "rescuer", + "reseal", + "research", + "reselect", + "reseller", + "resemble", + "resend", + "resent", + "reset", + "reshape", + "reshoot", + "reshuffle", + "residence", + "residency", + "resident", + "residual", + "residue", + "resigned", + "resilient", + "resistant", + "resisting", + "resize", + "resolute", + "resolved", + "resonant", + "resonate", + "resort", + "resource", + "respect", + "resubmit", + "result", + "resume", + "resupply", + "resurface", + "resurrect", + "retail", + "retainer", + "retaining", + "retake", + "retaliate", + "retention", + "rethink", + "retinal", + "retired", + "retiree", + "retiring", + "retold", + "retool", + "retorted", + "retouch", + "retrace", + "retract", + "retrain", + "retread", + "retreat", + "retrial", + "retrieval", + "retriever", + "retry", + "return", + "retying", + "retype", + "reunion", + "reunite", + "reusable", + "reuse", + "reveal", + "reveler", + "revenge", + "revenue", + "reverb", + "revered", + "reverence", + "reverend", + "reversal", + "reverse", + "reversing", + "reversion", + "revert", + "revisable", + "revise", + "revision", + "revisit", + "revivable", + "revival", + "reviver", + "reviving", + "revocable", + "revoke", + "revolt", + "revolver", + "revolving", + "reward", + "rewash", + "rewind", + "rewire", + "reword", + "rework", + "rewrap", + "rewrite", + "rhyme", + "ribbon", + "ribcage", + "rice", + "riches", + "richly", + "richness", + "rickety", + "ricotta", + "riddance", + "ridden", + "ride", + "riding", + "rifling", + "rift", + "rigging", + "rigid", + "rigor", + "rimless", + "rimmed", + "rind", + "rink", + "rinse", + "rinsing", + "riot", + "ripcord", + "ripeness", + "ripening", + "ripping", + "ripple", + "rippling", + "riptide", + "rise", + "rising", + "risk", + "risotto", + "ritalin", + "ritzy", + "rival", + "riverbank", + "riverbed", + "riverboat", + "riverside", + "riveter", + "riveting", + "roamer", + "roaming", + "roast", + "robbing", + "robe", + "robin", + "robotics", + "robust", + "rockband", + "rocker", + "rocket", + "rockfish", + "rockiness", + "rocking", + "rocklike", + "rockslide", + "rockstar", + "rocky", + "rogue", + "roman", + "romp", + "rope", + "roping", + "roster", + "rosy", + "rotten", + "rotting", + "rotunda", + "roulette", + "rounding", + "roundish", + "roundness", + "roundup", + "roundworm", + "routine", + "routing", + "rover", + "roving", + "royal", + "rubbed", + "rubber", + "rubbing", + "rubble", + "rubdown", + "ruby", + "ruckus", + "rudder", + "rug", + "ruined", + "rule", + "rumble", + "rumbling", + "rummage", + "rumor", + "runaround", + "rundown", + "runner", + "running", + "runny", + "runt", + "runway", + "rupture", + "rural", + "ruse", + "rush", + "rust", + "rut", + "sabbath", + "sabotage", + "sacrament", + "sacred", + "sacrifice", + "sadden", + "saddlebag", + "saddled", + "saddling", + "sadly", + "sadness", + "safari", + "safeguard", + "safehouse", + "safely", + "safeness", + "saffron", + "saga", + "sage", + "sagging", + "saggy", + "said", + "saint", + "sake", + "salad", + "salami", + "salaried", + "salary", + "saline", + "salon", + "saloon", + "salsa", + "salt", + "salutary", + "salute", + "salvage", + "salvaging", + "salvation", + "same", + "sample", + "sampling", + "sanction", + "sanctity", + "sanctuary", + "sandal", + "sandbag", + "sandbank", + "sandbar", + "sandblast", + "sandbox", + "sanded", + "sandfish", + "sanding", + "sandlot", + "sandpaper", + "sandpit", + "sandstone", + "sandstorm", + "sandworm", + "sandy", + "sanitary", + "sanitizer", + "sank", + "santa", + "sapling", + "sappiness", + "sappy", + "sarcasm", + "sarcastic", + "sardine", + "sash", + "sasquatch", + "sassy", + "satchel", + "satiable", + "satin", + "satirical", + "satisfied", + "satisfy", + "saturate", + "saturday", + "sauciness", + "saucy", + "sauna", + "savage", + "savanna", + "saved", + "savings", + "savior", + "savor", + "saxophone", + "say", + "scabbed", + "scabby", + "scalded", + "scalding", + "scale", + "scaling", + "scallion", + "scallop", + "scalping", + "scam", + "scandal", + "scanner", + "scanning", + "scant", + "scapegoat", + "scarce", + "scarcity", + "scarecrow", + "scared", + "scarf", + "scarily", + "scariness", + "scarring", + "scary", + "scavenger", + "scenic", + "schedule", + "schematic", + "scheme", + "scheming", + "schilling", + "schnapps", + "scholar", + "science", + "scientist", + "scion", + "scoff", + "scolding", + "scone", + "scoop", + "scooter", + "scope", + "scorch", + "scorebook", + "scorecard", + "scored", + "scoreless", + "scorer", + "scoring", + "scorn", + "scorpion", + "scotch", + "scoundrel", + "scoured", + "scouring", + "scouting", + "scouts", + "scowling", + "scrabble", + "scraggly", + "scrambled", + "scrambler", + "scrap", + "scratch", + "scrawny", + "screen", + "scribble", + "scribe", + "scribing", + "scrimmage", + "script", + "scroll", + "scrooge", + "scrounger", + "scrubbed", + "scrubber", + "scruffy", + "scrunch", + "scrutiny", + "scuba", + "scuff", + "sculptor", + "sculpture", + "scurvy", + "scuttle", + "secluded", + "secluding", + "seclusion", + "second", + "secrecy", + "secret", + "sectional", + "sector", + "secular", + "securely", + "security", + "sedan", + "sedate", + "sedation", + "sedative", + "sediment", + "seduce", + "seducing", + "segment", + "seismic", + "seizing", + "seldom", + "selected", + "selection", + "selective", + "selector", + "self", + "seltzer", + "semantic", + "semester", + "semicolon", + "semifinal", + "seminar", + "semisoft", + "semisweet", + "senate", + "senator", + "send", + "senior", + "senorita", + "sensation", + "sensitive", + "sensitize", + "sensually", + "sensuous", + "sepia", + "september", + "septic", + "septum", + "sequel", + "sequence", + "sequester", + "series", + "sermon", + "serotonin", + "serpent", + "serrated", + "serve", + "service", + "serving", + "sesame", + "sessions", + "setback", + "setting", + "settle", + "settling", + "setup", + "sevenfold", + "seventeen", + "seventh", + "seventy", + "severity", + "shabby", + "shack", + "shaded", + "shadily", + "shadiness", + "shading", + "shadow", + "shady", + "shaft", + "shakable", + "shakily", + "shakiness", + "shaking", + "shaky", + "shale", + "shallot", + "shallow", + "shame", + "shampoo", + "shamrock", + "shank", + "shanty", + "shape", + "shaping", + "share", + "sharpener", + "sharper", + "sharpie", + "sharply", + "sharpness", + "shawl", + "sheath", + "shed", + "sheep", + "sheet", + "shelf", + "shell", + "shelter", + "shelve", + "shelving", + "sherry", + "shield", + "shifter", + "shifting", + "shiftless", + "shifty", + "shimmer", + "shimmy", + "shindig", + "shine", + "shingle", + "shininess", + "shining", + "shiny", + "ship", + "shirt", + "shivering", + "shock", + "shone", + "shoplift", + "shopper", + "shopping", + "shoptalk", + "shore", + "shortage", + "shortcake", + "shortcut", + "shorten", + "shorter", + "shorthand", + "shortlist", + "shortly", + "shortness", + "shorts", + "shortwave", + "shorty", + "shout", + "shove", + "showbiz", + "showcase", + "showdown", + "shower", + "showgirl", + "showing", + "showman", + "shown", + "showoff", + "showpiece", + "showplace", + "showroom", + "showy", + "shrank", + "shrapnel", + "shredder", + "shredding", + "shrewdly", + "shriek", + "shrill", + "shrimp", + "shrine", + "shrink", + "shrivel", + "shrouded", + "shrubbery", + "shrubs", + "shrug", + "shrunk", + "shucking", + "shudder", + "shuffle", + "shuffling", + "shun", + "shush", + "shut", + "shy", + "siamese", + "siberian", + "sibling", + "siding", + "sierra", + "siesta", + "sift", + "sighing", + "silenced", + "silencer", + "silent", + "silica", + "silicon", + "silk", + "silliness", + "silly", + "silo", + "silt", + "silver", + "similarly", + "simile", + "simmering", + "simple", + "simplify", + "simply", + "sincere", + "sincerity", + "singer", + "singing", + "single", + "singular", + "sinister", + "sinless", + "sinner", + "sinuous", + "sip", + "siren", + "sister", + "sitcom", + "sitter", + "sitting", + "situated", + "situation", + "sixfold", + "sixteen", + "sixth", + "sixties", + "sixtieth", + "sixtyfold", + "sizable", + "sizably", + "size", + "sizing", + "sizzle", + "sizzling", + "skater", + "skating", + "skedaddle", + "skeletal", + "skeleton", + "skeptic", + "sketch", + "skewed", + "skewer", + "skid", + "skied", + "skier", + "skies", + "skiing", + "skilled", + "skillet", + "skillful", + "skimmed", + "skimmer", + "skimming", + "skimpily", + "skincare", + "skinhead", + "skinless", + "skinning", + "skinny", + "skintight", + "skipper", + "skipping", + "skirmish", + "skirt", + "skittle", + "skydiver", + "skylight", + "skyline", + "skype", + "skyrocket", + "skyward", + "slab", + "slacked", + "slacker", + "slacking", + "slackness", + "slacks", + "slain", + "slam", + "slander", + "slang", + "slapping", + "slapstick", + "slashed", + "slashing", + "slate", + "slather", + "slaw", + "sled", + "sleek", + "sleep", + "sleet", + "sleeve", + "slept", + "sliceable", + "sliced", + "slicer", + "slicing", + "slick", + "slider", + "slideshow", + "sliding", + "slighted", + "slighting", + "slightly", + "slimness", + "slimy", + "slinging", + "slingshot", + "slinky", + "slip", + "slit", + "sliver", + "slobbery", + "slogan", + "sloped", + "sloping", + "sloppily", + "sloppy", + "slot", + "slouching", + "slouchy", + "sludge", + "slug", + "slum", + "slurp", + "slush", + "sly", + "small", + "smartly", + "smartness", + "smasher", + "smashing", + "smashup", + "smell", + "smelting", + "smile", + "smilingly", + "smirk", + "smite", + "smith", + "smitten", + "smock", + "smog", + "smoked", + "smokeless", + "smokiness", + "smoking", + "smoky", + "smolder", + "smooth", + "smother", + "smudge", + "smudgy", + "smuggler", + "smuggling", + "smugly", + "smugness", + "snack", + "snagged", + "snaking", + "snap", + "snare", + "snarl", + "snazzy", + "sneak", + "sneer", + "sneeze", + "sneezing", + "snide", + "sniff", + "snippet", + "snipping", + "snitch", + "snooper", + "snooze", + "snore", + "snoring", + "snorkel", + "snort", + "snout", + "snowbird", + "snowboard", + "snowbound", + "snowcap", + "snowdrift", + "snowdrop", + "snowfall", + "snowfield", + "snowflake", + "snowiness", + "snowless", + "snowman", + "snowplow", + "snowshoe", + "snowstorm", + "snowsuit", + "snowy", + "snub", + "snuff", + "snuggle", + "snugly", + "snugness", + "speak", + "spearfish", + "spearhead", + "spearman", + "spearmint", + "species", + "specimen", + "specked", + "speckled", + "specks", + "spectacle", + "spectator", + "spectrum", + "speculate", + "speech", + "speed", + "spellbind", + "speller", + "spelling", + "spendable", + "spender", + "spending", + "spent", + "spew", + "sphere", + "spherical", + "sphinx", + "spider", + "spied", + "spiffy", + "spill", + "spilt", + "spinach", + "spinal", + "spindle", + "spinner", + "spinning", + "spinout", + "spinster", + "spiny", + "spiral", + "spirited", + "spiritism", + "spirits", + "spiritual", + "splashed", + "splashing", + "splashy", + "splatter", + "spleen", + "splendid", + "splendor", + "splice", + "splicing", + "splinter", + "splotchy", + "splurge", + "spoilage", + "spoiled", + "spoiler", + "spoiling", + "spoils", + "spoken", + "spokesman", + "sponge", + "spongy", + "sponsor", + "spoof", + "spookily", + "spooky", + "spool", + "spoon", + "spore", + "sporting", + "sports", + "sporty", + "spotless", + "spotlight", + "spotted", + "spotter", + "spotting", + "spotty", + "spousal", + "spouse", + "spout", + "sprain", + "sprang", + "sprawl", + "spray", + "spree", + "sprig", + "spring", + "sprinkled", + "sprinkler", + "sprint", + "sprite", + "sprout", + "spruce", + "sprung", + "spry", + "spud", + "spur", + "sputter", + "spyglass", + "squabble", + "squad", + "squall", + "squander", + "squash", + "squatted", + "squatter", + "squatting", + "squeak", + "squealer", + "squealing", + "squeamish", + "squeegee", + "squeeze", + "squeezing", + "squid", + "squiggle", + "squiggly", + "squint", + "squire", + "squirt", + "squishier", + "squishy", + "stability", + "stabilize", + "stable", + "stack", + "stadium", + "staff", + "stage", + "staging", + "stagnant", + "stagnate", + "stainable", + "stained", + "staining", + "stainless", + "stalemate", + "staleness", + "stalling", + "stallion", + "stamina", + "stammer", + "stamp", + "stand", + "stank", + "staple", + "stapling", + "starboard", + "starch", + "stardom", + "stardust", + "starfish", + "stargazer", + "staring", + "stark", + "starless", + "starlet", + "starlight", + "starlit", + "starring", + "starry", + "starship", + "starter", + "starting", + "startle", + "startling", + "startup", + "starved", + "starving", + "stash", + "state", + "static", + "statistic", + "statue", + "stature", + "status", + "statute", + "statutory", + "staunch", + "stays", + "steadfast", + "steadier", + "steadily", + "steadying", + "steam", + "steed", + "steep", + "steerable", + "steering", + "steersman", + "stegosaur", + "stellar", + "stem", + "stench", + "stencil", + "step", + "stereo", + "sterile", + "sterility", + "sterilize", + "sterling", + "sternness", + "sternum", + "stew", + "stick", + "stiffen", + "stiffly", + "stiffness", + "stifle", + "stifling", + "stillness", + "stilt", + "stimulant", + "stimulate", + "stimuli", + "stimulus", + "stinger", + "stingily", + "stinging", + "stingray", + "stingy", + "stinking", + "stinky", + "stipend", + "stipulate", + "stir", + "stitch", + "stock", + "stoic", + "stoke", + "stole", + "stomp", + "stonewall", + "stoneware", + "stonework", + "stoning", + "stony", + "stood", + "stooge", + "stool", + "stoop", + "stoplight", + "stoppable", + "stoppage", + "stopped", + "stopper", + "stopping", + "stopwatch", + "storable", + "storage", + "storeroom", + "storewide", + "storm", + "stout", + "stove", + "stowaway", + "stowing", + "straddle", + "straggler", + "strained", + "strainer", + "straining", + "strangely", + "stranger", + "strangle", + "strategic", + "strategy", + "stratus", + "straw", + "stray", + "streak", + "stream", + "street", + "strength", + "strenuous", + "strep", + "stress", + "stretch", + "strewn", + "stricken", + "strict", + "stride", + "strife", + "strike", + "striking", + "strive", + "striving", + "strobe", + "strode", + "stroller", + "strongbox", + "strongly", + "strongman", + "struck", + "structure", + "strudel", + "struggle", + "strum", + "strung", + "strut", + "stubbed", + "stubble", + "stubbly", + "stubborn", + "stucco", + "stuck", + "student", + "studied", + "studio", + "study", + "stuffed", + "stuffing", + "stuffy", + "stumble", + "stumbling", + "stump", + "stung", + "stunned", + "stunner", + "stunning", + "stunt", + "stupor", + "sturdily", + "sturdy", + "styling", + "stylishly", + "stylist", + "stylized", + "stylus", + "suave", + "subarctic", + "subatomic", + "subdivide", + "subdued", + "subduing", + "subfloor", + "subgroup", + "subheader", + "subject", + "sublease", + "sublet", + "sublevel", + "sublime", + "submarine", + "submerge", + "submersed", + "submitter", + "subpanel", + "subpar", + "subplot", + "subprime", + "subscribe", + "subscript", + "subsector", + "subside", + "subsiding", + "subsidize", + "subsidy", + "subsoil", + "subsonic", + "substance", + "subsystem", + "subtext", + "subtitle", + "subtly", + "subtotal", + "subtract", + "subtype", + "suburb", + "subway", + "subwoofer", + "subzero", + "succulent", + "such", + "suction", + "sudden", + "sudoku", + "suds", + "sufferer", + "suffering", + "suffice", + "suffix", + "suffocate", + "suffrage", + "sugar", + "suggest", + "suing", + "suitable", + "suitably", + "suitcase", + "suitor", + "sulfate", + "sulfide", + "sulfite", + "sulfur", + "sulk", + "sullen", + "sulphate", + "sulphuric", + "sultry", + "superbowl", + "superglue", + "superhero", + "superior", + "superjet", + "superman", + "supermom", + "supernova", + "supervise", + "supper", + "supplier", + "supply", + "support", + "supremacy", + "supreme", + "surcharge", + "surely", + "sureness", + "surface", + "surfacing", + "surfboard", + "surfer", + "surgery", + "surgical", + "surging", + "surname", + "surpass", + "surplus", + "surprise", + "surreal", + "surrender", + "surrogate", + "surround", + "survey", + "survival", + "survive", + "surviving", + "survivor", + "sushi", + "suspect", + "suspend", + "suspense", + "sustained", + "sustainer", + "swab", + "swaddling", + "swagger", + "swampland", + "swan", + "swapping", + "swarm", + "sway", + "swear", + "sweat", + "sweep", + "swell", + "swept", + "swerve", + "swifter", + "swiftly", + "swiftness", + "swimmable", + "swimmer", + "swimming", + "swimsuit", + "swimwear", + "swinger", + "swinging", + "swipe", + "swirl", + "switch", + "swivel", + "swizzle", + "swooned", + "swoop", + "swoosh", + "swore", + "sworn", + "swung", + "sycamore", + "sympathy", + "symphonic", + "symphony", + "symptom", + "synapse", + "syndrome", + "synergy", + "synopses", + "synopsis", + "synthesis", + "synthetic", + "syrup", + "system", + "t-shirt", + "tabasco", + "tabby", + "tableful", + "tables", + "tablet", + "tableware", + "tabloid", + "tackiness", + "tacking", + "tackle", + "tackling", + "tacky", + "taco", + "tactful", + "tactical", + "tactics", + "tactile", + "tactless", + "tadpole", + "taekwondo", + "tag", + "tainted", + "take", + "taking", + "talcum", + "talisman", + "tall", + "talon", + "tamale", + "tameness", + "tamer", + "tamper", + "tank", + "tanned", + "tannery", + "tanning", + "tantrum", + "tapeless", + "tapered", + "tapering", + "tapestry", + "tapioca", + "tapping", + "taps", + "tarantula", + "target", + "tarmac", + "tarnish", + "tarot", + "tartar", + "tartly", + "tartness", + "task", + "tassel", + "taste", + "tastiness", + "tasting", + "tasty", + "tattered", + "tattle", + "tattling", + "tattoo", + "taunt", + "tavern", + "thank", + "that", + "thaw", + "theater", + "theatrics", + "thee", + "theft", + "theme", + "theology", + "theorize", + "thermal", + "thermos", + "thesaurus", + "these", + "thesis", + "thespian", + "thicken", + "thicket", + "thickness", + "thieving", + "thievish", + "thigh", + "thimble", + "thing", + "think", + "thinly", + "thinner", + "thinness", + "thinning", + "thirstily", + "thirsting", + "thirsty", + "thirteen", + "thirty", + "thong", + "thorn", + "those", + "thousand", + "thrash", + "thread", + "threaten", + "threefold", + "thrift", + "thrill", + "thrive", + "thriving", + "throat", + "throbbing", + "throng", + "throttle", + "throwaway", + "throwback", + "thrower", + "throwing", + "thud", + "thumb", + "thumping", + "thursday", + "thus", + "thwarting", + "thyself", + "tiara", + "tibia", + "tidal", + "tidbit", + "tidiness", + "tidings", + "tidy", + "tiger", + "tighten", + "tightly", + "tightness", + "tightrope", + "tightwad", + "tigress", + "tile", + "tiling", + "till", + "tilt", + "timid", + "timing", + "timothy", + "tinderbox", + "tinfoil", + "tingle", + "tingling", + "tingly", + "tinker", + "tinkling", + "tinsel", + "tinsmith", + "tint", + "tinwork", + "tiny", + "tipoff", + "tipped", + "tipper", + "tipping", + "tiptoeing", + "tiptop", + "tiring", + "tissue", + "trace", + "tracing", + "track", + "traction", + "tractor", + "trade", + "trading", + "tradition", + "traffic", + "tragedy", + "trailing", + "trailside", + "train", + "traitor", + "trance", + "tranquil", + "transfer", + "transform", + "translate", + "transpire", + "transport", + "transpose", + "trapdoor", + "trapeze", + "trapezoid", + "trapped", + "trapper", + "trapping", + "traps", + "trash", + "travel", + "traverse", + "travesty", + "tray", + "treachery", + "treading", + "treadmill", + "treason", + "treat", + "treble", + "tree", + "trekker", + "tremble", + "trembling", + "tremor", + "trench", + "trend", + "trespass", + "triage", + "trial", + "triangle", + "tribesman", + "tribunal", + "tribune", + "tributary", + "tribute", + "triceps", + "trickery", + "trickily", + "tricking", + "trickle", + "trickster", + "tricky", + "tricolor", + "tricycle", + "trident", + "tried", + "trifle", + "trifocals", + "trillion", + "trilogy", + "trimester", + "trimmer", + "trimming", + "trimness", + "trinity", + "trio", + "tripod", + "tripping", + "triumph", + "trivial", + "trodden", + "trolling", + "trombone", + "trophy", + "tropical", + "tropics", + "trouble", + "troubling", + "trough", + "trousers", + "trout", + "trowel", + "truce", + "truck", + "truffle", + "trump", + "trunks", + "trustable", + "trustee", + "trustful", + "trusting", + "trustless", + "truth", + "try", + "tubby", + "tubeless", + "tubular", + "tucking", + "tuesday", + "tug", + "tuition", + "tulip", + "tumble", + "tumbling", + "tummy", + "turban", + "turbine", + "turbofan", + "turbojet", + "turbulent", + "turf", + "turkey", + "turmoil", + "turret", + "turtle", + "tusk", + "tutor", + "tutu", + "tux", + "tweak", + "tweed", + "tweet", + "tweezers", + "twelve", + "twentieth", + "twenty", + "twerp", + "twice", + "twiddle", + "twiddling", + "twig", + "twilight", + "twine", + "twins", + "twirl", + "twistable", + "twisted", + "twister", + "twisting", + "twisty", + "twitch", + "twitter", + "tycoon", + "tying", + "tyke", + "udder", + "ultimate", + "ultimatum", + "ultra", + "umbilical", + "umbrella", + "umpire", + "unabashed", + "unable", + "unadorned", + "unadvised", + "unafraid", + "unaired", + "unaligned", + "unaltered", + "unarmored", + "unashamed", + "unaudited", + "unawake", + "unaware", + "unbaked", + "unbalance", + "unbeaten", + "unbend", + "unbent", + "unbiased", + "unbitten", + "unblended", + "unblessed", + "unblock", + "unbolted", + "unbounded", + "unboxed", + "unbraided", + "unbridle", + "unbroken", + "unbuckled", + "unbundle", + "unburned", + "unbutton", + "uncanny", + "uncapped", + "uncaring", + "uncertain", + "unchain", + "unchanged", + "uncharted", + "uncheck", + "uncivil", + "unclad", + "unclaimed", + "unclamped", + "unclasp", + "uncle", + "unclip", + "uncloak", + "unclog", + "unclothed", + "uncoated", + "uncoiled", + "uncolored", + "uncombed", + "uncommon", + "uncooked", + "uncork", + "uncorrupt", + "uncounted", + "uncouple", + "uncouth", + "uncover", + "uncross", + "uncrown", + "uncrushed", + "uncured", + "uncurious", + "uncurled", + "uncut", + "undamaged", + "undated", + "undaunted", + "undead", + "undecided", + "undefined", + "underage", + "underarm", + "undercoat", + "undercook", + "undercut", + "underdog", + "underdone", + "underfed", + "underfeed", + "underfoot", + "undergo", + "undergrad", + "underhand", + "underline", + "underling", + "undermine", + "undermost", + "underpaid", + "underpass", + "underpay", + "underrate", + "undertake", + "undertone", + "undertook", + "undertow", + "underuse", + "underwear", + "underwent", + "underwire", + "undesired", + "undiluted", + "undivided", + "undocked", + "undoing", + "undone", + "undrafted", + "undress", + "undrilled", + "undusted", + "undying", + "unearned", + "unearth", + "unease", + "uneasily", + "uneasy", + "uneatable", + "uneaten", + "unedited", + "unelected", + "unending", + "unengaged", + "unenvied", + "unequal", + "unethical", + "uneven", + "unexpired", + "unexposed", + "unfailing", + "unfair", + "unfasten", + "unfazed", + "unfeeling", + "unfiled", + "unfilled", + "unfitted", + "unfitting", + "unfixable", + "unfixed", + "unflawed", + "unfocused", + "unfold", + "unfounded", + "unframed", + "unfreeze", + "unfrosted", + "unfrozen", + "unfunded", + "unglazed", + "ungloved", + "unglue", + "ungodly", + "ungraded", + "ungreased", + "unguarded", + "unguided", + "unhappily", + "unhappy", + "unharmed", + "unhealthy", + "unheard", + "unhearing", + "unheated", + "unhelpful", + "unhidden", + "unhinge", + "unhitched", + "unholy", + "unhook", + "unicorn", + "unicycle", + "unified", + "unifier", + "uniformed", + "uniformly", + "unify", + "unimpeded", + "uninjured", + "uninstall", + "uninsured", + "uninvited", + "union", + "uniquely", + "unisexual", + "unison", + "unissued", + "unit", + "universal", + "universe", + "unjustly", + "unkempt", + "unkind", + "unknotted", + "unknowing", + "unknown", + "unlaced", + "unlatch", + "unlawful", + "unleaded", + "unlearned", + "unleash", + "unless", + "unleveled", + "unlighted", + "unlikable", + "unlimited", + "unlined", + "unlinked", + "unlisted", + "unlit", + "unlivable", + "unloaded", + "unloader", + "unlocked", + "unlocking", + "unlovable", + "unloved", + "unlovely", + "unloving", + "unluckily", + "unlucky", + "unmade", + "unmanaged", + "unmanned", + "unmapped", + "unmarked", + "unmasked", + "unmasking", + "unmatched", + "unmindful", + "unmixable", + "unmixed", + "unmolded", + "unmoral", + "unmovable", + "unmoved", + "unmoving", + "unnamable", + "unnamed", + "unnatural", + "unneeded", + "unnerve", + "unnerving", + "unnoticed", + "unopened", + "unopposed", + "unpack", + "unpadded", + "unpaid", + "unpainted", + "unpaired", + "unpaved", + "unpeeled", + "unpicked", + "unpiloted", + "unpinned", + "unplanned", + "unplanted", + "unpleased", + "unpledged", + "unplowed", + "unplug", + "unpopular", + "unproven", + "unquote", + "unranked", + "unrated", + "unraveled", + "unreached", + "unread", + "unreal", + "unreeling", + "unrefined", + "unrelated", + "unrented", + "unrest", + "unretired", + "unrevised", + "unrigged", + "unripe", + "unrivaled", + "unroasted", + "unrobed", + "unroll", + "unruffled", + "unruly", + "unrushed", + "unsaddle", + "unsafe", + "unsaid", + "unsalted", + "unsaved", + "unsavory", + "unscathed", + "unscented", + "unscrew", + "unsealed", + "unseated", + "unsecured", + "unseeing", + "unseemly", + "unseen", + "unselect", + "unselfish", + "unsent", + "unsettled", + "unshackle", + "unshaken", + "unshaved", + "unshaven", + "unsheathe", + "unshipped", + "unsightly", + "unsigned", + "unskilled", + "unsliced", + "unsmooth", + "unsnap", + "unsocial", + "unsoiled", + "unsold", + "unsolved", + "unsorted", + "unspoiled", + "unspoken", + "unstable", + "unstaffed", + "unstamped", + "unsteady", + "unsterile", + "unstirred", + "unstitch", + "unstopped", + "unstuck", + "unstuffed", + "unstylish", + "unsubtle", + "unsubtly", + "unsuited", + "unsure", + "unsworn", + "untagged", + "untainted", + "untaken", + "untamed", + "untangled", + "untapped", + "untaxed", + "unthawed", + "unthread", + "untidy", + "untie", + "until", + "untimed", + "untimely", + "untitled", + "untoasted", + "untold", + "untouched", + "untracked", + "untrained", + "untreated", + "untried", + "untrimmed", + "untrue", + "untruth", + "unturned", + "untwist", + "untying", + "unusable", + "unused", + "unusual", + "unvalued", + "unvaried", + "unvarying", + "unveiled", + "unveiling", + "unvented", + "unviable", + "unvisited", + "unvocal", + "unwanted", + "unwarlike", + "unwary", + "unwashed", + "unwatched", + "unweave", + "unwed", + "unwelcome", + "unwell", + "unwieldy", + "unwilling", + "unwind", + "unwired", + "unwitting", + "unwomanly", + "unworldly", + "unworn", + "unworried", + "unworthy", + "unwound", + "unwoven", + "unwrapped", + "unwritten", + "unzip", + "upbeat", + "upchuck", + "upcoming", + "upcountry", + "update", + "upfront", + "upgrade", + "upheaval", + "upheld", + "uphill", + "uphold", + "uplifted", + "uplifting", + "upload", + "upon", + "upper", + "upright", + "uprising", + "upriver", + "uproar", + "uproot", + "upscale", + "upside", + "upstage", + "upstairs", + "upstart", + "upstate", + "upstream", + "upstroke", + "upswing", + "uptake", + "uptight", + "uptown", + "upturned", + "upward", + "upwind", + "uranium", + "urban", + "urchin", + "urethane", + "urgency", + "urgent", + "urging", + "urologist", + "urology", + "usable", + "usage", + "useable", + "used", + "uselessly", + "user", + "usher", + "usual", + "utensil", + "utility", + "utilize", + "utmost", + "utopia", + "utter", + "vacancy", + "vacant", + "vacate", + "vacation", + "vagabond", + "vagrancy", + "vagrantly", + "vaguely", + "vagueness", + "valiant", + "valid", + "valium", + "valley", + "valuables", + "value", + "vanilla", + "vanish", + "vanity", + "vanquish", + "vantage", + "vaporizer", + "variable", + "variably", + "varied", + "variety", + "various", + "varmint", + "varnish", + "varsity", + "varying", + "vascular", + "vaseline", + "vastly", + "vastness", + "veal", + "vegan", + "veggie", + "vehicular", + "velcro", + "velocity", + "velvet", + "vendetta", + "vending", + "vendor", + "veneering", + "vengeful", + "venomous", + "ventricle", + "venture", + "venue", + "venus", + "verbalize", + "verbally", + "verbose", + "verdict", + "verify", + "verse", + "version", + "versus", + "vertebrae", + "vertical", + "vertigo", + "very", + "vessel", + "vest", + "veteran", + "veto", + "vexingly", + "viability", + "viable", + "vibes", + "vice", + "vicinity", + "victory", + "video", + "viewable", + "viewer", + "viewing", + "viewless", + "viewpoint", + "vigorous", + "village", + "villain", + "vindicate", + "vineyard", + "vintage", + "violate", + "violation", + "violator", + "violet", + "violin", + "viper", + "viral", + "virtual", + "virtuous", + "virus", + "visa", + "viscosity", + "viscous", + "viselike", + "visible", + "visibly", + "vision", + "visiting", + "visitor", + "visor", + "vista", + "vitality", + "vitalize", + "vitally", + "vitamins", + "vivacious", + "vividly", + "vividness", + "vixen", + "vocalist", + "vocalize", + "vocally", + "vocation", + "voice", + "voicing", + "void", + "volatile", + "volley", + "voltage", + "volumes", + "voter", + "voting", + "voucher", + "vowed", + "vowel", + "voyage", + "wackiness", + "wad", + "wafer", + "waffle", + "waged", + "wager", + "wages", + "waggle", + "wagon", + "wake", + "waking", + "walk", + "walmart", + "walnut", + "walrus", + "waltz", + "wand", + "wannabe", + "wanted", + "wanting", + "wasabi", + "washable", + "washbasin", + "washboard", + "washbowl", + "washcloth", + "washday", + "washed", + "washer", + "washhouse", + "washing", + "washout", + "washroom", + "washstand", + "washtub", + "wasp", + "wasting", + "watch", + "water", + "waviness", + "waving", + "wavy", + "whacking", + "whacky", + "wham", + "wharf", + "wheat", + "whenever", + "whiff", + "whimsical", + "whinny", + "whiny", + "whisking", + "whoever", + "whole", + "whomever", + "whoopee", + "whooping", + "whoops", + "why", + "wick", + "widely", + "widen", + "widget", + "widow", + "width", + "wieldable", + "wielder", + "wife", + "wifi", + "wikipedia", + "wildcard", + "wildcat", + "wilder", + "wildfire", + "wildfowl", + "wildland", + "wildlife", + "wildly", + "wildness", + "willed", + "willfully", + "willing", + "willow", + "willpower", + "wilt", + "wimp", + "wince", + "wincing", + "wind", + "wing", + "winking", + "winner", + "winnings", + "winter", + "wipe", + "wired", + "wireless", + "wiring", + "wiry", + "wisdom", + "wise", + "wish", + "wisplike", + "wispy", + "wistful", + "wizard", + "wobble", + "wobbling", + "wobbly", + "wok", + "wolf", + "wolverine", + "womanhood", + "womankind", + "womanless", + "womanlike", + "womanly", + "womb", + "woof", + "wooing", + "wool", + "woozy", + "word", + "work", + "worried", + "worrier", + "worrisome", + "worry", + "worsening", + "worshiper", + "worst", + "wound", + "woven", + "wow", + "wrangle", + "wrath", + "wreath", + "wreckage", + "wrecker", + "wrecking", + "wrench", + "wriggle", + "wriggly", + "wrinkle", + "wrinkly", + "wrist", + "writing", + "written", + "wrongdoer", + "wronged", + "wrongful", + "wrongly", + "wrongness", + "wrought", + "xbox", + "xerox", + "yahoo", + "yam", + "yanking", + "yapping", + "yard", + "yarn", + "yeah", + "yearbook", + "yearling", + "yearly", + "yearning", + "yeast", + "yelling", + "yelp", + "yen", + "yesterday", + "yiddish", + "yield", + "yin", + "yippee", + "yo-yo", + "yodel", + "yoga", + "yogurt", + "yonder", + "yoyo", + "yummy", + "zap", + "zealous", + "zebra", + "zen", + "zeppelin", + "zero", + "zestfully", + "zesty", + "zigzagged", + "zipfile", + "zipping", + "zippy", + "zips", + "zit", + "zodiac", + "zombie", + "zone", + "zoning", + "zookeeper", + "zoologist", + "zoology", + "zoom", +]; diff --git a/jslib/common/src/models/api/cardApi.ts b/jslib/common/src/models/api/cardApi.ts new file mode 100644 index 00000000..8d972624 --- /dev/null +++ b/jslib/common/src/models/api/cardApi.ts @@ -0,0 +1,23 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class CardApi extends BaseResponse { + cardholderName: string; + brand: string; + number: string; + expMonth: string; + expYear: string; + code: string; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.cardholderName = this.getResponseProperty("CardholderName"); + this.brand = this.getResponseProperty("Brand"); + this.number = this.getResponseProperty("Number"); + this.expMonth = this.getResponseProperty("ExpMonth"); + this.expYear = this.getResponseProperty("ExpYear"); + this.code = this.getResponseProperty("Code"); + } +} diff --git a/jslib/common/src/models/api/fieldApi.ts b/jslib/common/src/models/api/fieldApi.ts new file mode 100644 index 00000000..cabdb7cc --- /dev/null +++ b/jslib/common/src/models/api/fieldApi.ts @@ -0,0 +1,21 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { BaseResponse } from "../response/baseResponse"; + +export class FieldApi extends BaseResponse { + name: string; + value: string; + type: FieldType; + linkedId: LinkedIdType; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.type = this.getResponseProperty("Type"); + this.name = this.getResponseProperty("Name"); + this.value = this.getResponseProperty("Value"); + this.linkedId = this.getResponseProperty("linkedId"); + } +} diff --git a/jslib/common/src/models/api/identityApi.ts b/jslib/common/src/models/api/identityApi.ts new file mode 100644 index 00000000..9312438f --- /dev/null +++ b/jslib/common/src/models/api/identityApi.ts @@ -0,0 +1,47 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class IdentityApi extends BaseResponse { + title: string; + firstName: string; + middleName: string; + lastName: string; + address1: string; + address2: string; + address3: string; + city: string; + state: string; + postalCode: string; + country: string; + company: string; + email: string; + phone: string; + ssn: string; + username: string; + passportNumber: string; + licenseNumber: string; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.title = this.getResponseProperty("Title"); + this.firstName = this.getResponseProperty("FirstName"); + this.middleName = this.getResponseProperty("MiddleName"); + this.lastName = this.getResponseProperty("LastName"); + this.address1 = this.getResponseProperty("Address1"); + this.address2 = this.getResponseProperty("Address2"); + this.address3 = this.getResponseProperty("Address3"); + this.city = this.getResponseProperty("City"); + this.state = this.getResponseProperty("State"); + this.postalCode = this.getResponseProperty("PostalCode"); + this.country = this.getResponseProperty("Country"); + this.company = this.getResponseProperty("Company"); + this.email = this.getResponseProperty("Email"); + this.phone = this.getResponseProperty("Phone"); + this.ssn = this.getResponseProperty("SSN"); + this.username = this.getResponseProperty("Username"); + this.passportNumber = this.getResponseProperty("PassportNumber"); + this.licenseNumber = this.getResponseProperty("LicenseNumber"); + } +} diff --git a/jslib/common/src/models/api/loginApi.ts b/jslib/common/src/models/api/loginApi.ts new file mode 100644 index 00000000..ddbe37ec --- /dev/null +++ b/jslib/common/src/models/api/loginApi.ts @@ -0,0 +1,29 @@ +import { BaseResponse } from "../response/baseResponse"; + +import { LoginUriApi } from "./loginUriApi"; + +export class LoginApi extends BaseResponse { + uris: LoginUriApi[]; + username: string; + password: string; + passwordRevisionDate: string; + totp: string; + autofillOnPageLoad: boolean; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.username = this.getResponseProperty("Username"); + this.password = this.getResponseProperty("Password"); + this.passwordRevisionDate = this.getResponseProperty("PasswordRevisionDate"); + this.totp = this.getResponseProperty("Totp"); + this.autofillOnPageLoad = this.getResponseProperty("AutofillOnPageLoad"); + + const uris = this.getResponseProperty("Uris"); + if (uris != null) { + this.uris = uris.map((u: any) => new LoginUriApi(u)); + } + } +} diff --git a/jslib/common/src/models/api/loginUriApi.ts b/jslib/common/src/models/api/loginUriApi.ts new file mode 100644 index 00000000..50ffb0da --- /dev/null +++ b/jslib/common/src/models/api/loginUriApi.ts @@ -0,0 +1,17 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { BaseResponse } from "../response/baseResponse"; + +export class LoginUriApi extends BaseResponse { + uri: string; + match: UriMatchType = null; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.uri = this.getResponseProperty("Uri"); + const match = this.getResponseProperty("Match"); + this.match = match != null ? match : null; + } +} diff --git a/jslib/common/src/models/api/permissionsApi.ts b/jslib/common/src/models/api/permissionsApi.ts new file mode 100644 index 00000000..bac79bd3 --- /dev/null +++ b/jslib/common/src/models/api/permissionsApi.ts @@ -0,0 +1,55 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class PermissionsApi extends BaseResponse { + accessEventLogs: boolean; + accessImportExport: boolean; + accessReports: boolean; + /** + * @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and + * `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0 + */ + manageAllCollections: boolean; + createNewCollections: boolean; + editAnyCollection: boolean; + deleteAnyCollection: boolean; + /** + * @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and + * `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0 + */ + manageAssignedCollections: boolean; + editAssignedCollections: boolean; + deleteAssignedCollections: boolean; + manageCiphers: boolean; + manageGroups: boolean; + manageSso: boolean; + managePolicies: boolean; + manageUsers: boolean; + manageResetPassword: boolean; + + constructor(data: any = null) { + super(data); + if (data == null) { + return this; + } + this.accessEventLogs = this.getResponseProperty("AccessEventLogs"); + this.accessImportExport = this.getResponseProperty("AccessImportExport"); + this.accessReports = this.getResponseProperty("AccessReports"); + + // For backwards compatibility with Server <= 1.43.0 + this.manageAllCollections = this.getResponseProperty("ManageAllCollections"); + this.manageAssignedCollections = this.getResponseProperty("ManageAssignedCollections"); + + this.createNewCollections = this.getResponseProperty("CreateNewCollections"); + this.editAnyCollection = this.getResponseProperty("EditAnyCollection"); + this.deleteAnyCollection = this.getResponseProperty("DeleteAnyCollection"); + this.editAssignedCollections = this.getResponseProperty("EditAssignedCollections"); + this.deleteAssignedCollections = this.getResponseProperty("DeleteAssignedCollections"); + + this.manageCiphers = this.getResponseProperty("ManageCiphers"); + this.manageGroups = this.getResponseProperty("ManageGroups"); + this.manageSso = this.getResponseProperty("ManageSso"); + this.managePolicies = this.getResponseProperty("ManagePolicies"); + this.manageUsers = this.getResponseProperty("ManageUsers"); + this.manageResetPassword = this.getResponseProperty("ManageResetPassword"); + } +} diff --git a/jslib/common/src/models/api/secureNoteApi.ts b/jslib/common/src/models/api/secureNoteApi.ts new file mode 100644 index 00000000..80f18c61 --- /dev/null +++ b/jslib/common/src/models/api/secureNoteApi.ts @@ -0,0 +1,14 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { BaseResponse } from "../response/baseResponse"; + +export class SecureNoteApi extends BaseResponse { + type: SecureNoteType; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.type = this.getResponseProperty("Type"); + } +} diff --git a/jslib/common/src/models/api/sendFileApi.ts b/jslib/common/src/models/api/sendFileApi.ts new file mode 100644 index 00000000..99fa0ff5 --- /dev/null +++ b/jslib/common/src/models/api/sendFileApi.ts @@ -0,0 +1,19 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class SendFileApi extends BaseResponse { + id: string; + fileName: string; + size: string; + sizeName: string; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.id = this.getResponseProperty("Id"); + this.fileName = this.getResponseProperty("FileName"); + this.size = this.getResponseProperty("Size"); + this.sizeName = this.getResponseProperty("SizeName"); + } +} diff --git a/jslib/common/src/models/api/sendTextApi.ts b/jslib/common/src/models/api/sendTextApi.ts new file mode 100644 index 00000000..56d21450 --- /dev/null +++ b/jslib/common/src/models/api/sendTextApi.ts @@ -0,0 +1,15 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class SendTextApi extends BaseResponse { + text: string; + hidden: boolean; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.text = this.getResponseProperty("Text"); + this.hidden = this.getResponseProperty("Hidden") || false; + } +} diff --git a/jslib/common/src/models/api/ssoConfigApi.ts b/jslib/common/src/models/api/ssoConfigApi.ts new file mode 100644 index 00000000..0767f3d6 --- /dev/null +++ b/jslib/common/src/models/api/ssoConfigApi.ts @@ -0,0 +1,136 @@ +import { + OpenIdConnectRedirectBehavior, + Saml2BindingType, + Saml2NameIdFormat, + Saml2SigningBehavior, + SsoType, +} from "../../enums/ssoEnums"; +import { BaseResponse } from "../response/baseResponse"; +import { SsoConfigView } from "../view/ssoConfigView"; + +export class SsoConfigApi extends BaseResponse { + static fromView(view: SsoConfigView, api = new SsoConfigApi()) { + api.configType = view.configType; + + api.keyConnectorEnabled = view.keyConnectorEnabled; + api.keyConnectorUrl = view.keyConnectorUrl; + + if (api.configType === SsoType.OpenIdConnect) { + api.authority = view.openId.authority; + api.clientId = view.openId.clientId; + api.clientSecret = view.openId.clientSecret; + api.metadataAddress = view.openId.metadataAddress; + api.redirectBehavior = view.openId.redirectBehavior; + api.getClaimsFromUserInfoEndpoint = view.openId.getClaimsFromUserInfoEndpoint; + api.additionalScopes = view.openId.additionalScopes; + api.additionalUserIdClaimTypes = view.openId.additionalUserIdClaimTypes; + api.additionalEmailClaimTypes = view.openId.additionalEmailClaimTypes; + api.additionalNameClaimTypes = view.openId.additionalNameClaimTypes; + api.acrValues = view.openId.acrValues; + api.expectedReturnAcrValue = view.openId.expectedReturnAcrValue; + } else if (api.configType === SsoType.Saml2) { + api.spNameIdFormat = view.saml.spNameIdFormat; + api.spOutboundSigningAlgorithm = view.saml.spOutboundSigningAlgorithm; + api.spSigningBehavior = view.saml.spSigningBehavior; + api.spMinIncomingSigningAlgorithm = view.saml.spMinIncomingSigningAlgorithm; + api.spWantAssertionsSigned = view.saml.spWantAssertionsSigned; + api.spValidateCertificates = view.saml.spValidateCertificates; + + api.idpEntityId = view.saml.idpEntityId; + api.idpBindingType = view.saml.idpBindingType; + api.idpSingleSignOnServiceUrl = view.saml.idpSingleSignOnServiceUrl; + api.idpSingleLogoutServiceUrl = view.saml.idpSingleLogoutServiceUrl; + api.idpX509PublicCert = view.saml.idpX509PublicCert; + api.idpOutboundSigningAlgorithm = view.saml.idpOutboundSigningAlgorithm; + api.idpAllowUnsolicitedAuthnResponse = view.saml.idpAllowUnsolicitedAuthnResponse; + api.idpWantAuthnRequestsSigned = view.saml.idpWantAuthnRequestsSigned; + + // Value is inverted in the api model (disable instead of allow) + api.idpDisableOutboundLogoutRequests = !view.saml.idpAllowOutboundLogoutRequests; + } + + return api; + } + configType: SsoType; + + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + // OpenId + authority: string; + clientId: string; + clientSecret: string; + metadataAddress: string; + redirectBehavior: OpenIdConnectRedirectBehavior; + getClaimsFromUserInfoEndpoint: boolean; + additionalScopes: string; + additionalUserIdClaimTypes: string; + additionalEmailClaimTypes: string; + additionalNameClaimTypes: string; + acrValues: string; + expectedReturnAcrValue: string; + + // SAML + spNameIdFormat: Saml2NameIdFormat; + spOutboundSigningAlgorithm: string; + spSigningBehavior: Saml2SigningBehavior; + spMinIncomingSigningAlgorithm: boolean; + spWantAssertionsSigned: boolean; + spValidateCertificates: boolean; + + idpEntityId: string; + idpBindingType: Saml2BindingType; + idpSingleSignOnServiceUrl: string; + idpSingleLogoutServiceUrl: string; + idpX509PublicCert: string; + idpOutboundSigningAlgorithm: string; + idpAllowUnsolicitedAuthnResponse: boolean; + idpDisableOutboundLogoutRequests: boolean; + idpWantAuthnRequestsSigned: boolean; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + + this.configType = this.getResponseProperty("ConfigType"); + + this.keyConnectorEnabled = this.getResponseProperty("KeyConnectorEnabled"); + this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); + + this.authority = this.getResponseProperty("Authority"); + this.clientId = this.getResponseProperty("ClientId"); + this.clientSecret = this.getResponseProperty("ClientSecret"); + this.metadataAddress = this.getResponseProperty("MetadataAddress"); + this.redirectBehavior = this.getResponseProperty("RedirectBehavior"); + this.getClaimsFromUserInfoEndpoint = this.getResponseProperty("GetClaimsFromUserInfoEndpoint"); + this.additionalScopes = this.getResponseProperty("AdditionalScopes"); + this.additionalUserIdClaimTypes = this.getResponseProperty("AdditionalUserIdClaimTypes"); + this.additionalEmailClaimTypes = this.getResponseProperty("AdditionalEmailClaimTypes"); + this.additionalNameClaimTypes = this.getResponseProperty("AdditionalNameClaimTypes"); + this.acrValues = this.getResponseProperty("AcrValues"); + this.expectedReturnAcrValue = this.getResponseProperty("ExpectedReturnAcrValue"); + + this.spNameIdFormat = this.getResponseProperty("SpNameIdFormat"); + this.spOutboundSigningAlgorithm = this.getResponseProperty("SpOutboundSigningAlgorithm"); + this.spSigningBehavior = this.getResponseProperty("SpSigningBehavior"); + this.spMinIncomingSigningAlgorithm = this.getResponseProperty("SpMinIncomingSigningAlgorithm"); + this.spWantAssertionsSigned = this.getResponseProperty("SpWantAssertionsSigned"); + this.spValidateCertificates = this.getResponseProperty("SpValidateCertificates"); + + this.idpEntityId = this.getResponseProperty("IdpEntityId"); + this.idpBindingType = this.getResponseProperty("IdpBindingType"); + this.idpSingleSignOnServiceUrl = this.getResponseProperty("IdpSingleSignOnServiceUrl"); + this.idpSingleLogoutServiceUrl = this.getResponseProperty("IdpSingleLogoutServiceUrl"); + this.idpX509PublicCert = this.getResponseProperty("IdpX509PublicCert"); + this.idpOutboundSigningAlgorithm = this.getResponseProperty("IdpOutboundSigningAlgorithm"); + this.idpAllowUnsolicitedAuthnResponse = this.getResponseProperty( + "IdpAllowUnsolicitedAuthnResponse" + ); + this.idpDisableOutboundLogoutRequests = this.getResponseProperty( + "IdpDisableOutboundLogoutRequests" + ); + this.idpWantAuthnRequestsSigned = this.getResponseProperty("IdpWantAuthnRequestsSigned"); + } +} diff --git a/jslib/common/src/models/data/attachmentData.ts b/jslib/common/src/models/data/attachmentData.ts new file mode 100644 index 00000000..50af03ac --- /dev/null +++ b/jslib/common/src/models/data/attachmentData.ts @@ -0,0 +1,22 @@ +import { AttachmentResponse } from "../response/attachmentResponse"; + +export class AttachmentData { + id: string; + url: string; + fileName: string; + key: string; + size: string; + sizeName: string; + + constructor(response?: AttachmentResponse) { + if (response == null) { + return; + } + this.id = response.id; + this.url = response.url; + this.fileName = response.fileName; + this.key = response.key; + this.size = response.size; + this.sizeName = response.sizeName; + } +} diff --git a/jslib/common/src/models/data/cardData.ts b/jslib/common/src/models/data/cardData.ts new file mode 100644 index 00000000..9d90e4b2 --- /dev/null +++ b/jslib/common/src/models/data/cardData.ts @@ -0,0 +1,23 @@ +import { CardApi } from "../api/cardApi"; + +export class CardData { + cardholderName: string; + brand: string; + number: string; + expMonth: string; + expYear: string; + code: string; + + constructor(data?: CardApi) { + if (data == null) { + return; + } + + this.cardholderName = data.cardholderName; + this.brand = data.brand; + this.number = data.number; + this.expMonth = data.expMonth; + this.expYear = data.expYear; + this.code = data.code; + } +} diff --git a/jslib/common/src/models/data/cipherData.ts b/jslib/common/src/models/data/cipherData.ts new file mode 100644 index 00000000..7ebc7070 --- /dev/null +++ b/jslib/common/src/models/data/cipherData.ts @@ -0,0 +1,85 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { CipherResponse } from "../response/cipherResponse"; + +import { AttachmentData } from "./attachmentData"; +import { CardData } from "./cardData"; +import { FieldData } from "./fieldData"; +import { IdentityData } from "./identityData"; +import { LoginData } from "./loginData"; +import { PasswordHistoryData } from "./passwordHistoryData"; +import { SecureNoteData } from "./secureNoteData"; + +export class CipherData { + id: string; + organizationId: string; + folderId: string; + userId: string; + edit: boolean; + viewPassword: boolean; + organizationUseTotp: boolean; + favorite: boolean; + revisionDate: string; + type: CipherType; + name: string; + notes: string; + login?: LoginData; + secureNote?: SecureNoteData; + card?: CardData; + identity?: IdentityData; + fields?: FieldData[]; + attachments?: AttachmentData[]; + passwordHistory?: PasswordHistoryData[]; + collectionIds?: string[]; + deletedDate: string; + reprompt: CipherRepromptType; + + constructor(response?: CipherResponse, userId?: string, collectionIds?: string[]) { + if (response == null) { + return; + } + + this.id = response.id; + this.organizationId = response.organizationId; + this.folderId = response.folderId; + this.userId = userId; + this.edit = response.edit; + this.viewPassword = response.viewPassword; + this.organizationUseTotp = response.organizationUseTotp; + this.favorite = response.favorite; + this.revisionDate = response.revisionDate; + this.type = response.type; + this.name = response.name; + this.notes = response.notes; + this.collectionIds = collectionIds != null ? collectionIds : response.collectionIds; + this.deletedDate = response.deletedDate; + this.reprompt = response.reprompt; + + switch (this.type) { + case CipherType.Login: + this.login = new LoginData(response.login); + break; + case CipherType.SecureNote: + this.secureNote = new SecureNoteData(response.secureNote); + break; + case CipherType.Card: + this.card = new CardData(response.card); + break; + case CipherType.Identity: + this.identity = new IdentityData(response.identity); + break; + default: + break; + } + + if (response.fields != null) { + this.fields = response.fields.map((f) => new FieldData(f)); + } + if (response.attachments != null) { + this.attachments = response.attachments.map((a) => new AttachmentData(a)); + } + if (response.passwordHistory != null) { + this.passwordHistory = response.passwordHistory.map((ph) => new PasswordHistoryData(ph)); + } + } +} diff --git a/jslib/common/src/models/data/collectionData.ts b/jslib/common/src/models/data/collectionData.ts new file mode 100644 index 00000000..9e2607ed --- /dev/null +++ b/jslib/common/src/models/data/collectionData.ts @@ -0,0 +1,17 @@ +import { CollectionDetailsResponse } from "../response/collectionResponse"; + +export class CollectionData { + id: string; + organizationId: string; + name: string; + externalId: string; + readOnly: boolean; + + constructor(response: CollectionDetailsResponse) { + this.id = response.id; + this.organizationId = response.organizationId; + this.name = response.name; + this.externalId = response.externalId; + this.readOnly = response.readOnly; + } +} diff --git a/jslib/common/src/models/data/eventData.ts b/jslib/common/src/models/data/eventData.ts new file mode 100644 index 00000000..c0e38ddc --- /dev/null +++ b/jslib/common/src/models/data/eventData.ts @@ -0,0 +1,7 @@ +import { EventType } from "../../enums/eventType"; + +export class EventData { + type: EventType; + cipherId: string; + date: string; +} diff --git a/jslib/common/src/models/data/fieldData.ts b/jslib/common/src/models/data/fieldData.ts new file mode 100644 index 00000000..6bec9def --- /dev/null +++ b/jslib/common/src/models/data/fieldData.ts @@ -0,0 +1,20 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { FieldApi } from "../api/fieldApi"; + +export class FieldData { + type: FieldType; + name: string; + value: string; + linkedId: LinkedIdType; + + constructor(response?: FieldApi) { + if (response == null) { + return; + } + this.type = response.type; + this.name = response.name; + this.value = response.value; + this.linkedId = response.linkedId; + } +} diff --git a/jslib/common/src/models/data/folderData.ts b/jslib/common/src/models/data/folderData.ts new file mode 100644 index 00000000..459ba4de --- /dev/null +++ b/jslib/common/src/models/data/folderData.ts @@ -0,0 +1,15 @@ +import { FolderResponse } from "../response/folderResponse"; + +export class FolderData { + id: string; + userId: string; + name: string; + revisionDate: string; + + constructor(response: FolderResponse, userId: string) { + this.userId = userId; + this.name = response.name; + this.id = response.id; + this.revisionDate = response.revisionDate; + } +} diff --git a/jslib/common/src/models/data/identityData.ts b/jslib/common/src/models/data/identityData.ts new file mode 100644 index 00000000..02aa7eb6 --- /dev/null +++ b/jslib/common/src/models/data/identityData.ts @@ -0,0 +1,47 @@ +import { IdentityApi } from "../api/identityApi"; + +export class IdentityData { + title: string; + firstName: string; + middleName: string; + lastName: string; + address1: string; + address2: string; + address3: string; + city: string; + state: string; + postalCode: string; + country: string; + company: string; + email: string; + phone: string; + ssn: string; + username: string; + passportNumber: string; + licenseNumber: string; + + constructor(data?: IdentityApi) { + if (data == null) { + return; + } + + this.title = data.title; + this.firstName = data.firstName; + this.middleName = data.middleName; + this.lastName = data.lastName; + this.address1 = data.address1; + this.address2 = data.address2; + this.address3 = data.address3; + this.city = data.city; + this.state = data.state; + this.postalCode = data.postalCode; + this.country = data.country; + this.company = data.company; + this.email = data.email; + this.phone = data.phone; + this.ssn = data.ssn; + this.username = data.username; + this.passportNumber = data.passportNumber; + this.licenseNumber = data.licenseNumber; + } +} diff --git a/jslib/common/src/models/data/loginData.ts b/jslib/common/src/models/data/loginData.ts new file mode 100644 index 00000000..e51180c8 --- /dev/null +++ b/jslib/common/src/models/data/loginData.ts @@ -0,0 +1,28 @@ +import { LoginApi } from "../api/loginApi"; + +import { LoginUriData } from "./loginUriData"; + +export class LoginData { + uris: LoginUriData[]; + username: string; + password: string; + passwordRevisionDate: string; + totp: string; + autofillOnPageLoad: boolean; + + constructor(data?: LoginApi) { + if (data == null) { + return; + } + + this.username = data.username; + this.password = data.password; + this.passwordRevisionDate = data.passwordRevisionDate; + this.totp = data.totp; + this.autofillOnPageLoad = data.autofillOnPageLoad; + + if (data.uris) { + this.uris = data.uris.map((u) => new LoginUriData(u)); + } + } +} diff --git a/jslib/common/src/models/data/loginUriData.ts b/jslib/common/src/models/data/loginUriData.ts new file mode 100644 index 00000000..874db4e8 --- /dev/null +++ b/jslib/common/src/models/data/loginUriData.ts @@ -0,0 +1,15 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { LoginUriApi } from "../api/loginUriApi"; + +export class LoginUriData { + uri: string; + match: UriMatchType = null; + + constructor(data?: LoginUriApi) { + if (data == null) { + return; + } + this.uri = data.uri; + this.match = data.match; + } +} diff --git a/jslib/common/src/models/data/organizationData.ts b/jslib/common/src/models/data/organizationData.ts new file mode 100644 index 00000000..fe190fff --- /dev/null +++ b/jslib/common/src/models/data/organizationData.ts @@ -0,0 +1,78 @@ +import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { ProductType } from "../../enums/productType"; +import { PermissionsApi } from "../api/permissionsApi"; +import { ProfileOrganizationResponse } from "../response/profileOrganizationResponse"; + +export class OrganizationData { + id: string; + name: string; + status: OrganizationUserStatusType; + type: OrganizationUserType; + enabled: boolean; + usePolicies: boolean; + useGroups: boolean; + useDirectory: boolean; + useEvents: boolean; + useTotp: boolean; + use2fa: boolean; + useApi: boolean; + useSso: boolean; + useKeyConnector: boolean; + useResetPassword: boolean; + selfHost: boolean; + usersGetPremium: boolean; + seats: number; + maxCollections: number; + maxStorageGb?: number; + ssoBound: boolean; + identifier: string; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + userId: string; + hasPublicAndPrivateKeys: boolean; + providerId: string; + providerName: string; + isProviderUser: boolean; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + constructor(response: ProfileOrganizationResponse) { + this.id = response.id; + this.name = response.name; + this.status = response.status; + this.type = response.type; + this.enabled = response.enabled; + this.usePolicies = response.usePolicies; + this.useGroups = response.useGroups; + this.useDirectory = response.useDirectory; + this.useEvents = response.useEvents; + this.useTotp = response.useTotp; + this.use2fa = response.use2fa; + this.useApi = response.useApi; + this.useSso = response.useSso; + this.useKeyConnector = response.useKeyConnector; + this.useResetPassword = response.useResetPassword; + this.selfHost = response.selfHost; + this.usersGetPremium = response.usersGetPremium; + this.seats = response.seats; + this.maxCollections = response.maxCollections; + this.maxStorageGb = response.maxStorageGb; + this.ssoBound = response.ssoBound; + this.identifier = response.identifier; + this.permissions = response.permissions; + this.resetPasswordEnrolled = response.resetPasswordEnrolled; + this.userId = response.userId; + this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys; + this.providerId = response.providerId; + this.providerName = response.providerName; + this.familySponsorshipFriendlyName = response.familySponsorshipFriendlyName; + this.familySponsorshipAvailable = response.familySponsorshipAvailable; + this.planProductType = response.planProductType; + this.keyConnectorEnabled = response.keyConnectorEnabled; + this.keyConnectorUrl = response.keyConnectorUrl; + } +} diff --git a/jslib/common/src/models/data/passwordHistoryData.ts b/jslib/common/src/models/data/passwordHistoryData.ts new file mode 100644 index 00000000..72436f3d --- /dev/null +++ b/jslib/common/src/models/data/passwordHistoryData.ts @@ -0,0 +1,15 @@ +import { PasswordHistoryResponse } from "../response/passwordHistoryResponse"; + +export class PasswordHistoryData { + password: string; + lastUsedDate: string; + + constructor(response?: PasswordHistoryResponse) { + if (response == null) { + return; + } + + this.password = response.password; + this.lastUsedDate = response.lastUsedDate; + } +} diff --git a/jslib/common/src/models/data/policyData.ts b/jslib/common/src/models/data/policyData.ts new file mode 100644 index 00000000..c93b4040 --- /dev/null +++ b/jslib/common/src/models/data/policyData.ts @@ -0,0 +1,18 @@ +import { PolicyType } from "../../enums/policyType"; +import { PolicyResponse } from "../response/policyResponse"; + +export class PolicyData { + id: string; + organizationId: string; + type: PolicyType; + data: any; + enabled: boolean; + + constructor(response: PolicyResponse) { + this.id = response.id; + this.organizationId = response.organizationId; + this.type = response.type; + this.data = response.data; + this.enabled = response.enabled; + } +} diff --git a/jslib/common/src/models/data/providerData.ts b/jslib/common/src/models/data/providerData.ts new file mode 100644 index 00000000..5d141ed1 --- /dev/null +++ b/jslib/common/src/models/data/providerData.ts @@ -0,0 +1,23 @@ +import { ProviderUserStatusType } from "../../enums/providerUserStatusType"; +import { ProviderUserType } from "../../enums/providerUserType"; +import { ProfileProviderResponse } from "../response/profileProviderResponse"; + +export class ProviderData { + id: string; + name: string; + status: ProviderUserStatusType; + type: ProviderUserType; + enabled: boolean; + userId: string; + useEvents: boolean; + + constructor(response: ProfileProviderResponse) { + this.id = response.id; + this.name = response.name; + this.status = response.status; + this.type = response.type; + this.enabled = response.enabled; + this.userId = response.userId; + this.useEvents = response.useEvents; + } +} diff --git a/jslib/common/src/models/data/secureNoteData.ts b/jslib/common/src/models/data/secureNoteData.ts new file mode 100644 index 00000000..f536b2de --- /dev/null +++ b/jslib/common/src/models/data/secureNoteData.ts @@ -0,0 +1,14 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { SecureNoteApi } from "../api/secureNoteApi"; + +export class SecureNoteData { + type: SecureNoteType; + + constructor(data?: SecureNoteApi) { + if (data == null) { + return; + } + + this.type = data.type; + } +} diff --git a/jslib/common/src/models/data/sendData.ts b/jslib/common/src/models/data/sendData.ts new file mode 100644 index 00000000..18c007e3 --- /dev/null +++ b/jslib/common/src/models/data/sendData.ts @@ -0,0 +1,58 @@ +import { SendType } from "../../enums/sendType"; +import { SendResponse } from "../response/sendResponse"; + +import { SendFileData } from "./sendFileData"; +import { SendTextData } from "./sendTextData"; + +export class SendData { + id: string; + accessId: string; + userId: string; + type: SendType; + name: string; + notes: string; + file: SendFileData; + text: SendTextData; + key: string; + maxAccessCount?: number; + accessCount: number; + revisionDate: string; + expirationDate: string; + deletionDate: string; + password: string; + disabled: boolean; + hideEmail: boolean; + + constructor(response?: SendResponse, userId?: string) { + if (response == null) { + return; + } + + this.id = response.id; + this.accessId = response.accessId; + this.userId = userId; + this.type = response.type; + this.name = response.name; + this.notes = response.notes; + this.key = response.key; + this.maxAccessCount = response.maxAccessCount; + this.accessCount = response.accessCount; + this.revisionDate = response.revisionDate; + this.expirationDate = response.expirationDate; + this.deletionDate = response.deletionDate; + this.password = response.password; + this.disabled = response.disable; + this.hideEmail = response.hideEmail; + + switch (this.type) { + case SendType.Text: + this.text = new SendTextData(response.text); + break; + case SendType.File: + this.file = new SendFileData(response.file); + break; + default: + break; + } + } +} diff --git a/jslib/common/src/models/data/sendFileData.ts b/jslib/common/src/models/data/sendFileData.ts new file mode 100644 index 00000000..aaf868c1 --- /dev/null +++ b/jslib/common/src/models/data/sendFileData.ts @@ -0,0 +1,19 @@ +import { SendFileApi } from "../api/sendFileApi"; + +export class SendFileData { + id: string; + fileName: string; + size: string; + sizeName: string; + + constructor(data?: SendFileApi) { + if (data == null) { + return; + } + + this.id = data.id; + this.fileName = data.fileName; + this.size = data.size; + this.sizeName = data.sizeName; + } +} diff --git a/jslib/common/src/models/data/sendTextData.ts b/jslib/common/src/models/data/sendTextData.ts new file mode 100644 index 00000000..fedf2ed6 --- /dev/null +++ b/jslib/common/src/models/data/sendTextData.ts @@ -0,0 +1,15 @@ +import { SendTextApi } from "../api/sendTextApi"; + +export class SendTextData { + text: string; + hidden: boolean; + + constructor(data?: SendTextApi) { + if (data == null) { + return; + } + + this.text = data.text; + this.hidden = data.hidden; + } +} diff --git a/jslib/common/src/models/domain/account.ts b/jslib/common/src/models/domain/account.ts new file mode 100644 index 00000000..e03f9be3 --- /dev/null +++ b/jslib/common/src/models/domain/account.ts @@ -0,0 +1,175 @@ +import { AuthenticationStatus } from "../../enums/authenticationStatus"; +import { KdfType } from "../../enums/kdfType"; +import { UriMatchType } from "../../enums/uriMatchType"; +import { CipherData } from "../data/cipherData"; +import { CollectionData } from "../data/collectionData"; +import { EventData } from "../data/eventData"; +import { FolderData } from "../data/folderData"; +import { OrganizationData } from "../data/organizationData"; +import { PolicyData } from "../data/policyData"; +import { ProviderData } from "../data/providerData"; +import { SendData } from "../data/sendData"; +import { CipherView } from "../view/cipherView"; +import { CollectionView } from "../view/collectionView"; +import { FolderView } from "../view/folderView"; +import { SendView } from "../view/sendView"; + +import { EncString } from "./encString"; +import { EnvironmentUrls } from "./environmentUrls"; +import { GeneratedPasswordHistory } from "./generatedPasswordHistory"; +import { Policy } from "./policy"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class EncryptionPair { + encrypted?: TEncrypted; + decrypted?: TDecrypted; +} + +export class DataEncryptionPair { + encrypted?: { [id: string]: TEncrypted }; + decrypted?: TDecrypted[]; +} + +export class AccountData { + ciphers?: DataEncryptionPair = new DataEncryptionPair< + CipherData, + CipherView + >(); + folders?: DataEncryptionPair = new DataEncryptionPair< + FolderData, + FolderView + >(); + localData?: any; + sends?: DataEncryptionPair = new DataEncryptionPair(); + collections?: DataEncryptionPair = new DataEncryptionPair< + CollectionData, + CollectionView + >(); + policies?: DataEncryptionPair = new DataEncryptionPair(); + passwordGenerationHistory?: EncryptionPair< + GeneratedPasswordHistory[], + GeneratedPasswordHistory[] + > = new EncryptionPair(); + addEditCipherInfo?: any; + eventCollection?: EventData[]; + organizations?: { [id: string]: OrganizationData }; + providers?: { [id: string]: ProviderData }; +} + +export class AccountKeys { + cryptoMasterKey?: SymmetricCryptoKey; + cryptoMasterKeyAuto?: string; + cryptoMasterKeyB64?: string; + cryptoMasterKeyBiometric?: string; + cryptoSymmetricKey?: EncryptionPair = new EncryptionPair< + string, + SymmetricCryptoKey + >(); + organizationKeys?: EncryptionPair> = new EncryptionPair< + any, + Map + >(); + providerKeys?: EncryptionPair> = new EncryptionPair< + any, + Map + >(); + privateKey?: EncryptionPair = new EncryptionPair(); + legacyEtmKey?: SymmetricCryptoKey; + publicKey?: ArrayBuffer; + apiKeyClientSecret?: string; +} + +export class AccountProfile { + apiKeyClientId?: string; + authenticationStatus?: AuthenticationStatus; + convertAccountToKeyConnector?: boolean; + email?: string; + emailVerified?: boolean; + entityId?: string; + entityType?: string; + everBeenUnlocked?: boolean; + forcePasswordReset?: boolean; + hasPremiumPersonally?: boolean; + lastSync?: string; + userId?: string; + usesKeyConnector?: boolean; + keyHash?: string; + kdfIterations?: number; + kdfType?: KdfType; +} + +export class AccountSettings { + autoConfirmFingerPrints?: boolean; + autoFillOnPageLoadDefault?: boolean; + biometricLocked?: boolean; + biometricUnlock?: boolean; + clearClipboard?: number; + collapsedGroupings?: string[]; + defaultUriMatch?: UriMatchType; + disableAddLoginNotification?: boolean; + disableAutoBiometricsPrompt?: boolean; + disableAutoTotpCopy?: boolean; + disableBadgeCounter?: boolean; + disableChangedPasswordNotification?: boolean; + disableContextMenuItem?: boolean; + disableGa?: boolean; + dontShowCardsCurrentTab?: boolean; + dontShowIdentitiesCurrentTab?: boolean; + enableAlwaysOnTop?: boolean; + enableAutoFillOnPageLoad?: boolean; + enableBiometric?: boolean; + enableFullWidth?: boolean; + enableGravitars?: boolean; + environmentUrls: EnvironmentUrls = new EnvironmentUrls(); + equivalentDomains?: any; + minimizeOnCopyToClipboard?: boolean; + neverDomains?: { [id: string]: any }; + passwordGenerationOptions?: any; + usernameGenerationOptions?: any; + generatorOptions?: any; + pinProtected?: EncryptionPair = new EncryptionPair(); + protectedPin?: string; + settings?: any; // TODO: Merge whatever is going on here into the AccountSettings model properly + vaultTimeout?: number; + vaultTimeoutAction?: string = "lock"; +} + +export class AccountTokens { + accessToken?: string; + decodedToken?: any; + refreshToken?: string; + securityStamp?: string; +} + +export class Account { + data?: AccountData = new AccountData(); + keys?: AccountKeys = new AccountKeys(); + profile?: AccountProfile = new AccountProfile(); + settings?: AccountSettings = new AccountSettings(); + tokens?: AccountTokens = new AccountTokens(); + + constructor(init: Partial) { + Object.assign(this, { + data: { + ...new AccountData(), + ...init?.data, + }, + keys: { + ...new AccountKeys(), + ...init?.keys, + }, + profile: { + ...new AccountProfile(), + ...init?.profile, + }, + settings: { + ...new AccountSettings(), + ...init?.settings, + }, + tokens: { + ...new AccountTokens(), + ...init?.tokens, + }, + }); + } +} diff --git a/jslib/common/src/models/domain/attachment.ts b/jslib/common/src/models/domain/attachment.ts new file mode 100644 index 00000000..cbfffdcf --- /dev/null +++ b/jslib/common/src/models/domain/attachment.ts @@ -0,0 +1,87 @@ +import { CryptoService } from "../../abstractions/crypto.service"; +import { Utils } from "../../misc/utils"; +import { AttachmentData } from "../data/attachmentData"; +import { AttachmentView } from "../view/attachmentView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Attachment extends Domain { + id: string; + url: string; + size: string; + sizeName: string; // Readable size, ex: "4.2 KB" or "1.43 GB" + key: EncString; + fileName: EncString; + + constructor(obj?: AttachmentData) { + super(); + if (obj == null) { + return; + } + + this.size = obj.size; + this.buildDomainModel( + this, + obj, + { + id: null, + url: null, + sizeName: null, + fileName: null, + key: null, + }, + ["id", "url", "sizeName"] + ); + } + + async decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + const view = await this.decryptObj( + new AttachmentView(this), + { + fileName: null, + }, + orgId, + encKey + ); + + if (this.key != null) { + let cryptoService: CryptoService; + const containerService = (Utils.global as any).bitwardenContainerService; + if (containerService) { + cryptoService = containerService.getCryptoService(); + } else { + throw new Error("global bitwardenContainerService not initialized."); + } + + try { + const orgKey = await cryptoService.getOrgKey(orgId); + const decValue = await cryptoService.decryptToBytes(this.key, orgKey ?? encKey); + view.key = new SymmetricCryptoKey(decValue); + } catch (e) { + // TODO: error? + } + } + + return view; + } + + toAttachmentData(): AttachmentData { + const a = new AttachmentData(); + a.size = this.size; + this.buildDataModel( + this, + a, + { + id: null, + url: null, + sizeName: null, + fileName: null, + key: null, + }, + ["id", "url", "sizeName"] + ); + return a; + } +} diff --git a/jslib/common/src/models/domain/authResult.ts b/jslib/common/src/models/domain/authResult.ts new file mode 100644 index 00000000..0781f872 --- /dev/null +++ b/jslib/common/src/models/domain/authResult.ts @@ -0,0 +1,17 @@ +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; +import { Utils } from "../../misc/utils"; + +export class AuthResult { + captchaSiteKey = ""; + resetMasterPassword = false; + forcePasswordReset = false; + twoFactorProviders: Map = null; + + get requiresCaptcha() { + return !Utils.isNullOrWhitespace(this.captchaSiteKey); + } + + get requiresTwoFactor() { + return this.twoFactorProviders != null; + } +} diff --git a/jslib/common/src/models/domain/card.ts b/jslib/common/src/models/domain/card.ts new file mode 100644 index 00000000..59f73f90 --- /dev/null +++ b/jslib/common/src/models/domain/card.ts @@ -0,0 +1,65 @@ +import { CardData } from "../data/cardData"; +import { CardView } from "../view/cardView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Card extends Domain { + cardholderName: EncString; + brand: EncString; + number: EncString; + expMonth: EncString; + expYear: EncString; + code: EncString; + + constructor(obj?: CardData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new CardView(), + { + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }, + orgId, + encKey + ); + } + + toCardData(): CardData { + const c = new CardData(); + this.buildDataModel(this, c, { + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }); + return c; + } +} diff --git a/jslib/common/src/models/domain/cipher.ts b/jslib/common/src/models/domain/cipher.ts new file mode 100644 index 00000000..23f98686 --- /dev/null +++ b/jslib/common/src/models/domain/cipher.ts @@ -0,0 +1,238 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { CipherData } from "../data/cipherData"; +import { CipherView } from "../view/cipherView"; + +import { Attachment } from "./attachment"; +import { Card } from "./card"; +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { Field } from "./field"; +import { Identity } from "./identity"; +import { Login } from "./login"; +import { Password } from "./password"; +import { SecureNote } from "./secureNote"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Cipher extends Domain { + id: string; + organizationId: string; + folderId: string; + name: EncString; + notes: EncString; + type: CipherType; + favorite: boolean; + organizationUseTotp: boolean; + edit: boolean; + viewPassword: boolean; + revisionDate: Date; + localData: any; + login: Login; + identity: Identity; + card: Card; + secureNote: SecureNote; + attachments: Attachment[]; + fields: Field[]; + passwordHistory: Password[]; + collectionIds: string[]; + deletedDate: Date; + reprompt: CipherRepromptType; + + constructor(obj?: CipherData, localData: any = null) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + userId: null, + organizationId: null, + folderId: null, + name: null, + notes: null, + }, + ["id", "userId", "organizationId", "folderId"] + ); + + this.type = obj.type; + this.favorite = obj.favorite; + this.organizationUseTotp = obj.organizationUseTotp; + this.edit = obj.edit; + if (obj.viewPassword != null) { + this.viewPassword = obj.viewPassword; + } else { + this.viewPassword = true; // Default for already synced Ciphers without viewPassword + } + this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null; + this.collectionIds = obj.collectionIds; + this.localData = localData; + this.deletedDate = obj.deletedDate != null ? new Date(obj.deletedDate) : null; + this.reprompt = obj.reprompt; + + switch (this.type) { + case CipherType.Login: + this.login = new Login(obj.login); + break; + case CipherType.SecureNote: + this.secureNote = new SecureNote(obj.secureNote); + break; + case CipherType.Card: + this.card = new Card(obj.card); + break; + case CipherType.Identity: + this.identity = new Identity(obj.identity); + break; + default: + break; + } + + if (obj.attachments != null) { + this.attachments = obj.attachments.map((a) => new Attachment(a)); + } else { + this.attachments = null; + } + + if (obj.fields != null) { + this.fields = obj.fields.map((f) => new Field(f)); + } else { + this.fields = null; + } + + if (obj.passwordHistory != null) { + this.passwordHistory = obj.passwordHistory.map((ph) => new Password(ph)); + } else { + this.passwordHistory = null; + } + } + + async decrypt(encKey?: SymmetricCryptoKey): Promise { + const model = new CipherView(this); + + await this.decryptObj( + model, + { + name: null, + notes: null, + }, + this.organizationId, + encKey + ); + + switch (this.type) { + case CipherType.Login: + model.login = await this.login.decrypt(this.organizationId, encKey); + break; + case CipherType.SecureNote: + model.secureNote = await this.secureNote.decrypt(this.organizationId, encKey); + break; + case CipherType.Card: + model.card = await this.card.decrypt(this.organizationId, encKey); + break; + case CipherType.Identity: + model.identity = await this.identity.decrypt(this.organizationId, encKey); + break; + default: + break; + } + + const orgId = this.organizationId; + + if (this.attachments != null && this.attachments.length > 0) { + const attachments: any[] = []; + await this.attachments.reduce((promise, attachment) => { + return promise + .then(() => { + return attachment.decrypt(orgId, encKey); + }) + .then((decAttachment) => { + attachments.push(decAttachment); + }); + }, Promise.resolve()); + model.attachments = attachments; + } + + if (this.fields != null && this.fields.length > 0) { + const fields: any[] = []; + await this.fields.reduce((promise, field) => { + return promise + .then(() => { + return field.decrypt(orgId, encKey); + }) + .then((decField) => { + fields.push(decField); + }); + }, Promise.resolve()); + model.fields = fields; + } + + if (this.passwordHistory != null && this.passwordHistory.length > 0) { + const passwordHistory: any[] = []; + await this.passwordHistory.reduce((promise, ph) => { + return promise + .then(() => { + return ph.decrypt(orgId, encKey); + }) + .then((decPh) => { + passwordHistory.push(decPh); + }); + }, Promise.resolve()); + model.passwordHistory = passwordHistory; + } + + return model; + } + + toCipherData(userId: string): CipherData { + const c = new CipherData(); + c.id = this.id; + c.organizationId = this.organizationId; + c.folderId = this.folderId; + c.userId = this.organizationId != null ? userId : null; + c.edit = this.edit; + c.viewPassword = this.viewPassword; + c.organizationUseTotp = this.organizationUseTotp; + c.favorite = this.favorite; + c.revisionDate = this.revisionDate != null ? this.revisionDate.toISOString() : null; + c.type = this.type; + c.collectionIds = this.collectionIds; + c.deletedDate = this.deletedDate != null ? this.deletedDate.toISOString() : null; + c.reprompt = this.reprompt; + + this.buildDataModel(this, c, { + name: null, + notes: null, + }); + + switch (c.type) { + case CipherType.Login: + c.login = this.login.toLoginData(); + break; + case CipherType.SecureNote: + c.secureNote = this.secureNote.toSecureNoteData(); + break; + case CipherType.Card: + c.card = this.card.toCardData(); + break; + case CipherType.Identity: + c.identity = this.identity.toIdentityData(); + break; + default: + break; + } + + if (this.fields != null) { + c.fields = this.fields.map((f) => f.toFieldData()); + } + if (this.attachments != null) { + c.attachments = this.attachments.map((a) => a.toAttachmentData()); + } + if (this.passwordHistory != null) { + c.passwordHistory = this.passwordHistory.map((ph) => ph.toPasswordHistoryData()); + } + return c; + } +} diff --git a/jslib/common/src/models/domain/collection.ts b/jslib/common/src/models/domain/collection.ts new file mode 100644 index 00000000..6f737cd4 --- /dev/null +++ b/jslib/common/src/models/domain/collection.ts @@ -0,0 +1,45 @@ +import { CollectionData } from "../data/collectionData"; +import { CollectionView } from "../view/collectionView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; + +export class Collection extends Domain { + id: string; + organizationId: string; + name: EncString; + externalId: string; + readOnly: boolean; + hidePasswords: boolean; + + constructor(obj?: CollectionData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + organizationId: null, + name: null, + externalId: null, + readOnly: null, + hidePasswords: null, + }, + ["id", "organizationId", "externalId", "readOnly", "hidePasswords"] + ); + } + + decrypt(): Promise { + return this.decryptObj( + new CollectionView(this), + { + name: null, + }, + this.organizationId + ); + } +} diff --git a/jslib/common/src/models/domain/decryptParameters.ts b/jslib/common/src/models/domain/decryptParameters.ts new file mode 100644 index 00000000..09a4c1d8 --- /dev/null +++ b/jslib/common/src/models/domain/decryptParameters.ts @@ -0,0 +1,8 @@ +export class DecryptParameters { + encKey: T; + data: T; + iv: T; + macKey: T; + mac: T; + macData: T; +} diff --git a/jslib/common/src/models/domain/domainBase.ts b/jslib/common/src/models/domain/domainBase.ts new file mode 100644 index 00000000..0c9e5ad6 --- /dev/null +++ b/jslib/common/src/models/domain/domainBase.ts @@ -0,0 +1,82 @@ +import { View } from "../view/view"; + +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export default class Domain { + protected buildDomainModel( + domain: D, + dataObj: any, + map: any, + notEncList: any[] = [] + ) { + for (const prop in map) { + // eslint-disable-next-line + if (!map.hasOwnProperty(prop)) { + continue; + } + + const objProp = dataObj[map[prop] || prop]; + if (notEncList.indexOf(prop) > -1) { + (domain as any)[prop] = objProp ? objProp : null; + } else { + (domain as any)[prop] = objProp ? new EncString(objProp) : null; + } + } + } + protected buildDataModel( + domain: D, + dataObj: any, + map: any, + notEncStringList: any[] = [] + ) { + for (const prop in map) { + // eslint-disable-next-line + if (!map.hasOwnProperty(prop)) { + continue; + } + + const objProp = (domain as any)[map[prop] || prop]; + if (notEncStringList.indexOf(prop) > -1) { + (dataObj as any)[prop] = objProp != null ? objProp : null; + } else { + (dataObj as any)[prop] = objProp != null ? (objProp as EncString).encryptedString : null; + } + } + } + + protected async decryptObj( + viewModel: T, + map: any, + orgId: string, + key: SymmetricCryptoKey = null + ): Promise { + const promises = []; + const self: any = this; + + for (const prop in map) { + // eslint-disable-next-line + if (!map.hasOwnProperty(prop)) { + continue; + } + + (function (theProp) { + const p = Promise.resolve() + .then(() => { + const mapProp = map[theProp] || theProp; + if (self[mapProp]) { + return self[mapProp].decrypt(orgId, key); + } + return null; + }) + .then((val: any) => { + (viewModel as any)[theProp] = val; + }); + promises.push(p); + })(prop); + } + + await Promise.all(promises); + return viewModel; + } +} diff --git a/jslib/common/src/models/domain/encArrayBuffer.ts b/jslib/common/src/models/domain/encArrayBuffer.ts new file mode 100644 index 00000000..97f47c39 --- /dev/null +++ b/jslib/common/src/models/domain/encArrayBuffer.ts @@ -0,0 +1,3 @@ +export class EncArrayBuffer { + constructor(public buffer: ArrayBuffer) {} +} diff --git a/jslib/common/src/models/domain/encString.ts b/jslib/common/src/models/domain/encString.ts new file mode 100644 index 00000000..74ae8370 --- /dev/null +++ b/jslib/common/src/models/domain/encString.ts @@ -0,0 +1,122 @@ +import { CryptoService } from "../../abstractions/crypto.service"; +import { EncryptionType } from "../../enums/encryptionType"; +import { Utils } from "../../misc/utils"; + +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class EncString { + encryptedString?: string; + encryptionType?: EncryptionType; + decryptedValue?: string; + data?: string; + iv?: string; + mac?: string; + + constructor( + encryptedStringOrType: string | EncryptionType, + data?: string, + iv?: string, + mac?: string + ) { + if (data != null) { + // data and header + const encType = encryptedStringOrType as EncryptionType; + + if (iv != null) { + this.encryptedString = encType + "." + iv + "|" + data; + } else { + this.encryptedString = encType + "." + data; + } + + // mac + if (mac != null) { + this.encryptedString += "|" + mac; + } + + this.encryptionType = encType; + this.data = data; + this.iv = iv; + this.mac = mac; + + return; + } + + this.encryptedString = encryptedStringOrType as string; + if (!this.encryptedString) { + return; + } + + const headerPieces = this.encryptedString.split("."); + let encPieces: string[] = null; + + if (headerPieces.length === 2) { + try { + this.encryptionType = parseInt(headerPieces[0], null); + encPieces = headerPieces[1].split("|"); + } catch (e) { + return; + } + } else { + encPieces = this.encryptedString.split("|"); + this.encryptionType = + encPieces.length === 3 + ? EncryptionType.AesCbc128_HmacSha256_B64 + : EncryptionType.AesCbc256_B64; + } + + switch (this.encryptionType) { + case EncryptionType.AesCbc128_HmacSha256_B64: + case EncryptionType.AesCbc256_HmacSha256_B64: + if (encPieces.length !== 3) { + return; + } + + this.iv = encPieces[0]; + this.data = encPieces[1]; + this.mac = encPieces[2]; + break; + case EncryptionType.AesCbc256_B64: + if (encPieces.length !== 2) { + return; + } + + this.iv = encPieces[0]; + this.data = encPieces[1]; + break; + case EncryptionType.Rsa2048_OaepSha256_B64: + case EncryptionType.Rsa2048_OaepSha1_B64: + if (encPieces.length !== 1) { + return; + } + + this.data = encPieces[0]; + break; + default: + return; + } + } + + async decrypt(orgId: string, key: SymmetricCryptoKey = null): Promise { + if (this.decryptedValue != null) { + return this.decryptedValue; + } + + let cryptoService: CryptoService; + const containerService = (Utils.global as any).bitwardenContainerService; + if (containerService) { + cryptoService = containerService.getCryptoService(); + } else { + throw new Error("global bitwardenContainerService not initialized."); + } + + try { + if (key == null) { + key = await cryptoService.getOrgKey(orgId); + } + this.decryptedValue = await cryptoService.decryptToUtf8(this, key); + } catch (e) { + this.decryptedValue = "[error: cannot decrypt]"; + } + return this.decryptedValue; + } +} diff --git a/jslib/common/src/models/domain/encryptedObject.ts b/jslib/common/src/models/domain/encryptedObject.ts new file mode 100644 index 00000000..5ce93dbe --- /dev/null +++ b/jslib/common/src/models/domain/encryptedObject.ts @@ -0,0 +1,8 @@ +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class EncryptedObject { + iv: ArrayBuffer; + data: ArrayBuffer; + mac: ArrayBuffer; + key: SymmetricCryptoKey; +} diff --git a/jslib/common/src/models/domain/environmentUrls.ts b/jslib/common/src/models/domain/environmentUrls.ts new file mode 100644 index 00000000..d4fd173c --- /dev/null +++ b/jslib/common/src/models/domain/environmentUrls.ts @@ -0,0 +1,10 @@ +export class EnvironmentUrls { + base: string = null; + api: string = null; + identity: string = null; + icons: string = null; + notifications: string = null; + events: string = null; + webVault: string = null; + keyConnector: string = null; +} diff --git a/jslib/common/src/models/domain/field.ts b/jslib/common/src/models/domain/field.ts new file mode 100644 index 00000000..71dc615a --- /dev/null +++ b/jslib/common/src/models/domain/field.ts @@ -0,0 +1,62 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { FieldData } from "../data/fieldData"; +import { FieldView } from "../view/fieldView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Field extends Domain { + name: EncString; + value: EncString; + type: FieldType; + linkedId: LinkedIdType; + + constructor(obj?: FieldData) { + super(); + if (obj == null) { + return; + } + + this.type = obj.type; + this.linkedId = obj.linkedId; + this.buildDomainModel( + this, + obj, + { + name: null, + value: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new FieldView(this), + { + name: null, + value: null, + }, + orgId, + encKey + ); + } + + toFieldData(): FieldData { + const f = new FieldData(); + this.buildDataModel( + this, + f, + { + name: null, + value: null, + type: null, + linkedId: null, + }, + ["type", "linkedId"] + ); + return f; + } +} diff --git a/jslib/common/src/models/domain/folder.ts b/jslib/common/src/models/domain/folder.ts new file mode 100644 index 00000000..8596e94d --- /dev/null +++ b/jslib/common/src/models/domain/folder.ts @@ -0,0 +1,40 @@ +import { FolderData } from "../data/folderData"; +import { FolderView } from "../view/folderView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; + +export class Folder extends Domain { + id: string; + name: EncString; + revisionDate: Date; + + constructor(obj?: FolderData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + name: null, + }, + ["id"] + ); + + this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null; + } + + decrypt(): Promise { + return this.decryptObj( + new FolderView(this), + { + name: null, + }, + null + ); + } +} diff --git a/jslib/common/src/models/domain/generatedPasswordHistory.ts b/jslib/common/src/models/domain/generatedPasswordHistory.ts new file mode 100644 index 00000000..b4cc9b22 --- /dev/null +++ b/jslib/common/src/models/domain/generatedPasswordHistory.ts @@ -0,0 +1,9 @@ +export class GeneratedPasswordHistory { + password: string; + date: number; + + constructor(password: string, date: number) { + this.password = password; + this.date = date; + } +} diff --git a/jslib/common/src/models/domain/globalState.ts b/jslib/common/src/models/domain/globalState.ts new file mode 100644 index 00000000..326ba11b --- /dev/null +++ b/jslib/common/src/models/domain/globalState.ts @@ -0,0 +1,40 @@ +import { StateVersion } from "../../enums/stateVersion"; +import { ThemeType } from "../../enums/themeType"; + +import { EnvironmentUrls } from "./environmentUrls"; +import { WindowState } from "./windowState"; + +export class GlobalState { + enableAlwaysOnTop?: boolean; + installedVersion?: string; + locale?: string = "en"; + organizationInvitation?: any; + ssoCodeVerifier?: string; + ssoOrganizationIdentifier?: string; + ssoState?: string; + rememberedEmail?: string; + theme?: ThemeType = ThemeType.System; + window?: WindowState = new WindowState(); + twoFactorToken?: string; + disableFavicon?: boolean; + biometricAwaitingAcceptance?: boolean; + biometricFingerprintValidated?: boolean; + vaultTimeout?: number; + vaultTimeoutAction?: string; + loginRedirect?: any; + mainWindowSize?: number; + enableBiometrics?: boolean; + biometricText?: string; + noAutoPromptBiometrics?: boolean; + noAutoPromptBiometricsText?: string; + stateVersion: StateVersion = StateVersion.One; + environmentUrls: EnvironmentUrls = new EnvironmentUrls(); + enableTray?: boolean; + enableMinimizeToTray?: boolean; + enableCloseToTray?: boolean; + enableStartToTray?: boolean; + openAtLogin?: boolean; + alwaysShowDock?: boolean; + enableBrowserIntegration?: boolean; + enableBrowserIntegrationFingerprint?: boolean; +} diff --git a/jslib/common/src/models/domain/identity.ts b/jslib/common/src/models/domain/identity.ts new file mode 100644 index 00000000..4af22849 --- /dev/null +++ b/jslib/common/src/models/domain/identity.ts @@ -0,0 +1,113 @@ +import { IdentityData } from "../data/identityData"; +import { IdentityView } from "../view/identityView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Identity extends Domain { + title: EncString; + firstName: EncString; + middleName: EncString; + lastName: EncString; + address1: EncString; + address2: EncString; + address3: EncString; + city: EncString; + state: EncString; + postalCode: EncString; + country: EncString; + company: EncString; + email: EncString; + phone: EncString; + ssn: EncString; + username: EncString; + passportNumber: EncString; + licenseNumber: EncString; + + constructor(obj?: IdentityData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + title: null, + firstName: null, + middleName: null, + lastName: null, + address1: null, + address2: null, + address3: null, + city: null, + state: null, + postalCode: null, + country: null, + company: null, + email: null, + phone: null, + ssn: null, + username: null, + passportNumber: null, + licenseNumber: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new IdentityView(), + { + title: null, + firstName: null, + middleName: null, + lastName: null, + address1: null, + address2: null, + address3: null, + city: null, + state: null, + postalCode: null, + country: null, + company: null, + email: null, + phone: null, + ssn: null, + username: null, + passportNumber: null, + licenseNumber: null, + }, + orgId, + encKey + ); + } + + toIdentityData(): IdentityData { + const i = new IdentityData(); + this.buildDataModel(this, i, { + title: null, + firstName: null, + middleName: null, + lastName: null, + address1: null, + address2: null, + address3: null, + city: null, + state: null, + postalCode: null, + country: null, + company: null, + email: null, + phone: null, + ssn: null, + username: null, + passportNumber: null, + licenseNumber: null, + }); + return i; + } +} diff --git a/jslib/common/src/models/domain/importResult.ts b/jslib/common/src/models/domain/importResult.ts new file mode 100644 index 00000000..695dbf9d --- /dev/null +++ b/jslib/common/src/models/domain/importResult.ts @@ -0,0 +1,14 @@ +import { CipherView } from "../view/cipherView"; +import { CollectionView } from "../view/collectionView"; +import { FolderView } from "../view/folderView"; + +export class ImportResult { + success = false; + missingPassword = false; + errorMessage: string; + ciphers: CipherView[] = []; + folders: FolderView[] = []; + folderRelationships: [number, number][] = []; + collections: CollectionView[] = []; + collectionRelationships: [number, number][] = []; +} diff --git a/jslib/common/src/models/domain/logInCredentials.ts b/jslib/common/src/models/domain/logInCredentials.ts new file mode 100644 index 00000000..c1e23610 --- /dev/null +++ b/jslib/common/src/models/domain/logInCredentials.ts @@ -0,0 +1,31 @@ +import { AuthenticationType } from "../../enums/authenticationType"; +import { TokenRequestTwoFactor } from "../request/identityToken/tokenRequestTwoFactor"; + +export class PasswordLogInCredentials { + readonly type = AuthenticationType.Password; + + constructor( + public email: string, + public masterPassword: string, + public captchaToken?: string, + public twoFactor?: TokenRequestTwoFactor + ) {} +} + +export class SsoLogInCredentials { + readonly type = AuthenticationType.Sso; + + constructor( + public code: string, + public codeVerifier: string, + public redirectUrl: string, + public orgId: string, + public twoFactor?: TokenRequestTwoFactor + ) {} +} + +export class ApiLogInCredentials { + readonly type = AuthenticationType.Api; + + constructor(public clientId: string, public clientSecret: string) {} +} diff --git a/jslib/common/src/models/domain/login.ts b/jslib/common/src/models/domain/login.ts new file mode 100644 index 00000000..76ba4020 --- /dev/null +++ b/jslib/common/src/models/domain/login.ts @@ -0,0 +1,88 @@ +import { LoginData } from "../data/loginData"; +import { LoginView } from "../view/loginView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { LoginUri } from "./loginUri"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Login extends Domain { + uris: LoginUri[]; + username: EncString; + password: EncString; + passwordRevisionDate?: Date; + totp: EncString; + autofillOnPageLoad: boolean; + + constructor(obj?: LoginData) { + super(); + if (obj == null) { + return; + } + + this.passwordRevisionDate = + obj.passwordRevisionDate != null ? new Date(obj.passwordRevisionDate) : null; + this.autofillOnPageLoad = obj.autofillOnPageLoad; + this.buildDomainModel( + this, + obj, + { + username: null, + password: null, + totp: null, + }, + [] + ); + + if (obj.uris) { + this.uris = []; + obj.uris.forEach((u) => { + this.uris.push(new LoginUri(u)); + }); + } + } + + async decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + const view = await this.decryptObj( + new LoginView(this), + { + username: null, + password: null, + totp: null, + }, + orgId, + encKey + ); + + if (this.uris != null) { + view.uris = []; + for (let i = 0; i < this.uris.length; i++) { + const uri = await this.uris[i].decrypt(orgId, encKey); + view.uris.push(uri); + } + } + + return view; + } + + toLoginData(): LoginData { + const l = new LoginData(); + l.passwordRevisionDate = + this.passwordRevisionDate != null ? this.passwordRevisionDate.toISOString() : null; + l.autofillOnPageLoad = this.autofillOnPageLoad; + this.buildDataModel(this, l, { + username: null, + password: null, + totp: null, + }); + + if (this.uris != null && this.uris.length > 0) { + l.uris = []; + this.uris.forEach((u) => { + l.uris.push(u.toLoginUriData()); + }); + } + + return l; + } +} diff --git a/jslib/common/src/models/domain/loginUri.ts b/jslib/common/src/models/domain/loginUri.ts new file mode 100644 index 00000000..9bd78c65 --- /dev/null +++ b/jslib/common/src/models/domain/loginUri.ts @@ -0,0 +1,54 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { LoginUriData } from "../data/loginUriData"; +import { LoginUriView } from "../view/loginUriView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class LoginUri extends Domain { + uri: EncString; + match: UriMatchType; + + constructor(obj?: LoginUriData) { + super(); + if (obj == null) { + return; + } + + this.match = obj.match; + this.buildDomainModel( + this, + obj, + { + uri: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new LoginUriView(this), + { + uri: null, + }, + orgId, + encKey + ); + } + + toLoginUriData(): LoginUriData { + const u = new LoginUriData(); + this.buildDataModel( + this, + u, + { + uri: null, + match: null, + }, + ["match"] + ); + return u; + } +} diff --git a/jslib/common/src/models/domain/masterPasswordPolicyOptions.ts b/jslib/common/src/models/domain/masterPasswordPolicyOptions.ts new file mode 100644 index 00000000..2207d00c --- /dev/null +++ b/jslib/common/src/models/domain/masterPasswordPolicyOptions.ts @@ -0,0 +1,10 @@ +import Domain from "./domainBase"; + +export class MasterPasswordPolicyOptions extends Domain { + minComplexity = 0; + minLength = 0; + requireUpper = false; + requireLower = false; + requireNumbers = false; + requireSpecial = false; +} diff --git a/jslib/common/src/models/domain/organization.ts b/jslib/common/src/models/domain/organization.ts new file mode 100644 index 00000000..8a682212 --- /dev/null +++ b/jslib/common/src/models/domain/organization.ts @@ -0,0 +1,184 @@ +import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { ProductType } from "../../enums/productType"; +import { PermissionsApi } from "../api/permissionsApi"; +import { OrganizationData } from "../data/organizationData"; + +export class Organization { + id: string; + name: string; + status: OrganizationUserStatusType; + type: OrganizationUserType; + enabled: boolean; + usePolicies: boolean; + useGroups: boolean; + useDirectory: boolean; + useEvents: boolean; + useTotp: boolean; + use2fa: boolean; + useApi: boolean; + useSso: boolean; + useKeyConnector: boolean; + useResetPassword: boolean; + selfHost: boolean; + usersGetPremium: boolean; + seats: number; + maxCollections: number; + maxStorageGb?: number; + ssoBound: boolean; + identifier: string; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + userId: string; + hasPublicAndPrivateKeys: boolean; + providerId: string; + providerName: string; + isProviderUser: boolean; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + constructor(obj?: OrganizationData) { + if (obj == null) { + return; + } + + this.id = obj.id; + this.name = obj.name; + this.status = obj.status; + this.type = obj.type; + this.enabled = obj.enabled; + this.usePolicies = obj.usePolicies; + this.useGroups = obj.useGroups; + this.useDirectory = obj.useDirectory; + this.useEvents = obj.useEvents; + this.useTotp = obj.useTotp; + this.use2fa = obj.use2fa; + this.useApi = obj.useApi; + this.useSso = obj.useSso; + this.useKeyConnector = obj.useKeyConnector; + this.useResetPassword = obj.useResetPassword; + this.selfHost = obj.selfHost; + this.usersGetPremium = obj.usersGetPremium; + this.seats = obj.seats; + this.maxCollections = obj.maxCollections; + this.maxStorageGb = obj.maxStorageGb; + this.ssoBound = obj.ssoBound; + this.identifier = obj.identifier; + this.permissions = obj.permissions; + this.resetPasswordEnrolled = obj.resetPasswordEnrolled; + this.userId = obj.userId; + this.hasPublicAndPrivateKeys = obj.hasPublicAndPrivateKeys; + this.providerId = obj.providerId; + this.providerName = obj.providerName; + this.isProviderUser = obj.isProviderUser; + this.familySponsorshipFriendlyName = obj.familySponsorshipFriendlyName; + this.familySponsorshipAvailable = obj.familySponsorshipAvailable; + this.planProductType = obj.planProductType; + this.keyConnectorEnabled = obj.keyConnectorEnabled; + this.keyConnectorUrl = obj.keyConnectorUrl; + } + + get canAccess() { + if (this.type === OrganizationUserType.Owner) { + return true; + } + return this.enabled && this.status === OrganizationUserStatusType.Confirmed; + } + + get isManager() { + return ( + this.type === OrganizationUserType.Manager || + this.type === OrganizationUserType.Owner || + this.type === OrganizationUserType.Admin + ); + } + + get isAdmin() { + return this.type === OrganizationUserType.Owner || this.type === OrganizationUserType.Admin; + } + + get isOwner() { + return this.type === OrganizationUserType.Owner || this.isProviderUser; + } + + get canAccessEventLogs() { + return this.isAdmin || this.permissions.accessEventLogs; + } + + get canAccessImportExport() { + return this.isAdmin || this.permissions.accessImportExport; + } + + get canAccessReports() { + return this.isAdmin || this.permissions.accessReports; + } + + get canCreateNewCollections() { + return ( + this.isManager || + (this.permissions.createNewCollections ?? this.permissions.manageAllCollections) + ); + } + + get canEditAnyCollection() { + return ( + this.isAdmin || (this.permissions.editAnyCollection ?? this.permissions.manageAllCollections) + ); + } + + get canDeleteAnyCollection() { + return ( + this.isAdmin || + (this.permissions.deleteAnyCollection ?? this.permissions.manageAllCollections) + ); + } + + get canViewAllCollections() { + return this.canCreateNewCollections || this.canEditAnyCollection || this.canDeleteAnyCollection; + } + + get canEditAssignedCollections() { + return ( + this.isManager || + (this.permissions.editAssignedCollections ?? this.permissions.manageAssignedCollections) + ); + } + + get canDeleteAssignedCollections() { + return ( + this.isManager || + (this.permissions.deleteAssignedCollections ?? this.permissions.manageAssignedCollections) + ); + } + + get canViewAssignedCollections() { + return this.canDeleteAssignedCollections || this.canEditAssignedCollections; + } + + get canManageGroups() { + return this.isAdmin || this.permissions.manageGroups; + } + + get canManageSso() { + return this.isAdmin || this.permissions.manageSso; + } + + get canManagePolicies() { + return this.isAdmin || this.permissions.managePolicies; + } + + get canManageUsers() { + return this.isAdmin || this.permissions.manageUsers; + } + + get canManageUsersPassword() { + return this.isAdmin || this.permissions.manageResetPassword; + } + + get isExemptFromPolicies() { + return this.canManagePolicies; + } +} diff --git a/jslib/common/src/models/domain/password.ts b/jslib/common/src/models/domain/password.ts new file mode 100644 index 00000000..ae47b18d --- /dev/null +++ b/jslib/common/src/models/domain/password.ts @@ -0,0 +1,43 @@ +import { PasswordHistoryData } from "../data/passwordHistoryData"; +import { PasswordHistoryView } from "../view/passwordHistoryView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Password extends Domain { + password: EncString; + lastUsedDate: Date; + + constructor(obj?: PasswordHistoryData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel(this, obj, { + password: null, + }); + this.lastUsedDate = new Date(obj.lastUsedDate); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new PasswordHistoryView(this), + { + password: null, + }, + orgId, + encKey + ); + } + + toPasswordHistoryData(): PasswordHistoryData { + const ph = new PasswordHistoryData(); + ph.lastUsedDate = this.lastUsedDate.toISOString(); + this.buildDataModel(this, ph, { + password: null, + }); + return ph; + } +} diff --git a/jslib/common/src/models/domain/passwordGeneratorPolicyOptions.ts b/jslib/common/src/models/domain/passwordGeneratorPolicyOptions.ts new file mode 100644 index 00000000..19072a9f --- /dev/null +++ b/jslib/common/src/models/domain/passwordGeneratorPolicyOptions.ts @@ -0,0 +1,31 @@ +import Domain from "./domainBase"; + +export class PasswordGeneratorPolicyOptions extends Domain { + defaultType = ""; + minLength = 0; + useUppercase = false; + useLowercase = false; + useNumbers = false; + numberCount = 0; + useSpecial = false; + specialCount = 0; + minNumberWords = 0; + capitalize = false; + includeNumber = false; + + inEffect() { + return ( + this.defaultType !== "" || + this.minLength > 0 || + this.numberCount > 0 || + this.specialCount > 0 || + this.useUppercase || + this.useLowercase || + this.useNumbers || + this.useSpecial || + this.minNumberWords > 0 || + this.capitalize || + this.includeNumber + ); + } +} diff --git a/jslib/common/src/models/domain/policy.ts b/jslib/common/src/models/domain/policy.ts new file mode 100644 index 00000000..c4a113cc --- /dev/null +++ b/jslib/common/src/models/domain/policy.ts @@ -0,0 +1,25 @@ +import { PolicyType } from "../../enums/policyType"; +import { PolicyData } from "../data/policyData"; + +import Domain from "./domainBase"; + +export class Policy extends Domain { + id: string; + organizationId: string; + type: PolicyType; + data: any; + enabled: boolean; + + constructor(obj?: PolicyData) { + super(); + if (obj == null) { + return; + } + + this.id = obj.id; + this.organizationId = obj.organizationId; + this.type = obj.type; + this.data = obj.data; + this.enabled = obj.enabled; + } +} diff --git a/jslib/common/src/models/domain/provider.ts b/jslib/common/src/models/domain/provider.ts new file mode 100644 index 00000000..6e14340b --- /dev/null +++ b/jslib/common/src/models/domain/provider.ts @@ -0,0 +1,50 @@ +import { ProviderUserStatusType } from "../../enums/providerUserStatusType"; +import { ProviderUserType } from "../../enums/providerUserType"; +import { ProviderData } from "../data/providerData"; + +export class Provider { + id: string; + name: string; + status: ProviderUserStatusType; + type: ProviderUserType; + enabled: boolean; + userId: string; + useEvents: boolean; + + constructor(obj?: ProviderData) { + if (obj == null) { + return; + } + + this.id = obj.id; + this.name = obj.name; + this.status = obj.status; + this.type = obj.type; + this.enabled = obj.enabled; + this.userId = obj.userId; + this.useEvents = obj.useEvents; + } + + get canAccess() { + if (this.isProviderAdmin) { + return true; + } + return this.enabled && this.status === ProviderUserStatusType.Confirmed; + } + + get canCreateOrganizations() { + return this.enabled && this.isProviderAdmin; + } + + get canManageUsers() { + return this.isProviderAdmin; + } + + get canAccessEventLogs() { + return this.isProviderAdmin; + } + + get isProviderAdmin() { + return this.type === ProviderUserType.ProviderAdmin; + } +} diff --git a/jslib/common/src/models/domain/resetPasswordPolicyOptions.ts b/jslib/common/src/models/domain/resetPasswordPolicyOptions.ts new file mode 100644 index 00000000..2ee11a5c --- /dev/null +++ b/jslib/common/src/models/domain/resetPasswordPolicyOptions.ts @@ -0,0 +1,5 @@ +import Domain from "./domainBase"; + +export class ResetPasswordPolicyOptions extends Domain { + autoEnrollEnabled = false; +} diff --git a/jslib/common/src/models/domain/secureNote.ts b/jslib/common/src/models/domain/secureNote.ts new file mode 100644 index 00000000..8bde3164 --- /dev/null +++ b/jslib/common/src/models/domain/secureNote.ts @@ -0,0 +1,29 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { SecureNoteData } from "../data/secureNoteData"; +import { SecureNoteView } from "../view/secureNoteView"; + +import Domain from "./domainBase"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class SecureNote extends Domain { + type: SecureNoteType; + + constructor(obj?: SecureNoteData) { + super(); + if (obj == null) { + return; + } + + this.type = obj.type; + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return Promise.resolve(new SecureNoteView(this)); + } + + toSecureNoteData(): SecureNoteData { + const n = new SecureNoteData(); + n.type = this.type; + return n; + } +} diff --git a/jslib/common/src/models/domain/send.ts b/jslib/common/src/models/domain/send.ts new file mode 100644 index 00000000..c2e769ec --- /dev/null +++ b/jslib/common/src/models/domain/send.ts @@ -0,0 +1,114 @@ +import { CryptoService } from "../../abstractions/crypto.service"; +import { SendType } from "../../enums/sendType"; +import { Utils } from "../../misc/utils"; +import { SendData } from "../data/sendData"; +import { SendView } from "../view/sendView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SendFile } from "./sendFile"; +import { SendText } from "./sendText"; + +export class Send extends Domain { + id: string; + accessId: string; + userId: string; + type: SendType; + name: EncString; + notes: EncString; + file: SendFile; + text: SendText; + key: EncString; + maxAccessCount?: number; + accessCount: number; + revisionDate: Date; + expirationDate: Date; + deletionDate: Date; + password: string; + disabled: boolean; + hideEmail: boolean; + + constructor(obj?: SendData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + accessId: null, + userId: null, + name: null, + notes: null, + key: null, + }, + ["id", "accessId", "userId"] + ); + + this.type = obj.type; + this.maxAccessCount = obj.maxAccessCount; + this.accessCount = obj.accessCount; + this.password = obj.password; + this.disabled = obj.disabled; + this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null; + this.deletionDate = obj.deletionDate != null ? new Date(obj.deletionDate) : null; + this.expirationDate = obj.expirationDate != null ? new Date(obj.expirationDate) : null; + this.hideEmail = obj.hideEmail; + + switch (this.type) { + case SendType.Text: + this.text = new SendText(obj.text); + break; + case SendType.File: + this.file = new SendFile(obj.file); + break; + default: + break; + } + } + + async decrypt(): Promise { + const model = new SendView(this); + + let cryptoService: CryptoService; + const containerService = (Utils.global as any).bitwardenContainerService; + if (containerService) { + cryptoService = containerService.getCryptoService(); + } else { + throw new Error("global bitwardenContainerService not initialized."); + } + + try { + model.key = await cryptoService.decryptToBytes(this.key, null); + model.cryptoKey = await cryptoService.makeSendKey(model.key); + } catch (e) { + // TODO: error? + } + + await this.decryptObj( + model, + { + name: null, + notes: null, + }, + null, + model.cryptoKey + ); + + switch (this.type) { + case SendType.File: + model.file = await this.file.decrypt(model.cryptoKey); + break; + case SendType.Text: + model.text = await this.text.decrypt(model.cryptoKey); + break; + default: + break; + } + + return model; + } +} diff --git a/jslib/common/src/models/domain/sendAccess.ts b/jslib/common/src/models/domain/sendAccess.ts new file mode 100644 index 00000000..dab68cc6 --- /dev/null +++ b/jslib/common/src/models/domain/sendAccess.ts @@ -0,0 +1,77 @@ +import { SendType } from "../../enums/sendType"; +import { SendAccessResponse } from "../response/sendAccessResponse"; +import { SendAccessView } from "../view/sendAccessView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SendFile } from "./sendFile"; +import { SendText } from "./sendText"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class SendAccess extends Domain { + id: string; + type: SendType; + name: EncString; + file: SendFile; + text: SendText; + expirationDate: Date; + creatorIdentifier: string; + + constructor(obj?: SendAccessResponse) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + name: null, + expirationDate: null, + creatorIdentifier: null, + }, + ["id", "expirationDate", "creatorIdentifier"] + ); + + this.type = obj.type; + + switch (this.type) { + case SendType.Text: + this.text = new SendText(obj.text); + break; + case SendType.File: + this.file = new SendFile(obj.file); + break; + default: + break; + } + } + + async decrypt(key: SymmetricCryptoKey): Promise { + const model = new SendAccessView(this); + + await this.decryptObj( + model, + { + name: null, + }, + null, + key + ); + + switch (this.type) { + case SendType.File: + model.file = await this.file.decrypt(key); + break; + case SendType.Text: + model.text = await this.text.decrypt(key); + break; + default: + break; + } + + return model; + } +} diff --git a/jslib/common/src/models/domain/sendFile.ts b/jslib/common/src/models/domain/sendFile.ts new file mode 100644 index 00000000..17799407 --- /dev/null +++ b/jslib/common/src/models/domain/sendFile.ts @@ -0,0 +1,44 @@ +import { SendFileData } from "../data/sendFileData"; +import { SendFileView } from "../view/sendFileView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class SendFile extends Domain { + id: string; + size: string; + sizeName: string; + fileName: EncString; + + constructor(obj?: SendFileData) { + super(); + if (obj == null) { + return; + } + + this.size = obj.size; + this.buildDomainModel( + this, + obj, + { + id: null, + sizeName: null, + fileName: null, + }, + ["id", "sizeName"] + ); + } + + async decrypt(key: SymmetricCryptoKey): Promise { + const view = await this.decryptObj( + new SendFileView(this), + { + fileName: null, + }, + null, + key + ); + return view; + } +} diff --git a/jslib/common/src/models/domain/sendText.ts b/jslib/common/src/models/domain/sendText.ts new file mode 100644 index 00000000..523e5ee5 --- /dev/null +++ b/jslib/common/src/models/domain/sendText.ts @@ -0,0 +1,39 @@ +import { SendTextData } from "../data/sendTextData"; +import { SendTextView } from "../view/sendTextView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class SendText extends Domain { + text: EncString; + hidden: boolean; + + constructor(obj?: SendTextData) { + super(); + if (obj == null) { + return; + } + + this.hidden = obj.hidden; + this.buildDomainModel( + this, + obj, + { + text: null, + }, + [] + ); + } + + decrypt(key: SymmetricCryptoKey): Promise { + return this.decryptObj( + new SendTextView(this), + { + text: null, + }, + null, + key + ); + } +} diff --git a/jslib/common/src/models/domain/sortedCiphersCache.ts b/jslib/common/src/models/domain/sortedCiphersCache.ts new file mode 100644 index 00000000..8c32744e --- /dev/null +++ b/jslib/common/src/models/domain/sortedCiphersCache.ts @@ -0,0 +1,87 @@ +import { CipherView } from "../view/cipherView"; + +const CacheTTL = 3000; + +export class SortedCiphersCache { + private readonly sortedCiphersByUrl: Map = new Map(); + private readonly timeouts: Map = new Map(); + + constructor(private readonly comparator: (a: CipherView, b: CipherView) => number) {} + + isCached(url: string) { + return this.sortedCiphersByUrl.has(url); + } + + addCiphers(url: string, ciphers: CipherView[]) { + ciphers.sort(this.comparator); + this.sortedCiphersByUrl.set(url, new Ciphers(ciphers)); + this.resetTimer(url); + } + + getLastUsed(url: string) { + this.resetTimer(url); + return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getLastUsed() : null; + } + + getLastLaunched(url: string) { + return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getLastLaunched() : null; + } + + getNext(url: string) { + this.resetTimer(url); + return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getNext() : null; + } + + updateLastUsedIndex(url: string) { + if (this.isCached(url)) { + this.sortedCiphersByUrl.get(url).updateLastUsedIndex(); + } + } + + clear() { + this.sortedCiphersByUrl.clear(); + this.timeouts.clear(); + } + + private resetTimer(url: string) { + clearTimeout(this.timeouts.get(url)); + this.timeouts.set( + url, + setTimeout(() => { + this.sortedCiphersByUrl.delete(url); + this.timeouts.delete(url); + }, CacheTTL) + ); + } +} + +class Ciphers { + lastUsedIndex = -1; + + constructor(private readonly ciphers: CipherView[]) {} + + getLastUsed() { + this.lastUsedIndex = Math.max(this.lastUsedIndex, 0); + return this.ciphers[this.lastUsedIndex]; + } + + getLastLaunched() { + const usedCiphers = this.ciphers.filter((cipher) => cipher.localData?.lastLaunched); + const sortedCiphers = usedCiphers.sort( + (x, y) => y.localData.lastLaunched.valueOf() - x.localData.lastLaunched.valueOf() + ); + return sortedCiphers[0]; + } + + getNextIndex() { + return (this.lastUsedIndex + 1) % this.ciphers.length; + } + + getNext() { + return this.ciphers[this.getNextIndex()]; + } + + updateLastUsedIndex() { + this.lastUsedIndex = this.getNextIndex(); + } +} diff --git a/jslib/common/src/models/domain/state.ts b/jslib/common/src/models/domain/state.ts new file mode 100644 index 00000000..f5a2c046 --- /dev/null +++ b/jslib/common/src/models/domain/state.ts @@ -0,0 +1,17 @@ +import { Account } from "./account"; +import { GlobalState } from "./globalState"; + +export class State< + TGlobalState extends GlobalState = GlobalState, + TAccount extends Account = Account +> { + accounts: { [userId: string]: TAccount } = {}; + globals: TGlobalState; + activeUserId: string; + authenticatedAccounts: string[] = []; + accountActivity: { [userId: string]: number } = {}; + + constructor(globals: TGlobalState) { + this.globals = globals; + } +} diff --git a/jslib/common/src/models/domain/storageOptions.ts b/jslib/common/src/models/domain/storageOptions.ts new file mode 100644 index 00000000..2db7e0cc --- /dev/null +++ b/jslib/common/src/models/domain/storageOptions.ts @@ -0,0 +1,10 @@ +import { HtmlStorageLocation } from "../../enums/htmlStorageLocation"; +import { StorageLocation } from "../../enums/storageLocation"; + +export type StorageOptions = { + storageLocation?: StorageLocation; + useSecureStorage?: boolean; + userId?: string; + htmlStorageLocation?: HtmlStorageLocation; + keySuffix?: string; +}; diff --git a/jslib/common/src/models/domain/symmetricCryptoKey.ts b/jslib/common/src/models/domain/symmetricCryptoKey.ts new file mode 100644 index 00000000..a58dc6fd --- /dev/null +++ b/jslib/common/src/models/domain/symmetricCryptoKey.ts @@ -0,0 +1,57 @@ +import { EncryptionType } from "../../enums/encryptionType"; +import { Utils } from "../../misc/utils"; + +export class SymmetricCryptoKey { + key: ArrayBuffer; + encKey?: ArrayBuffer; + macKey?: ArrayBuffer; + encType: EncryptionType; + + keyB64: string; + encKeyB64: string; + macKeyB64: string; + + meta: any; + + constructor(key: ArrayBuffer, encType?: EncryptionType) { + if (key == null) { + throw new Error("Must provide key"); + } + + if (encType == null) { + if (key.byteLength === 32) { + encType = EncryptionType.AesCbc256_B64; + } else if (key.byteLength === 64) { + encType = EncryptionType.AesCbc256_HmacSha256_B64; + } else { + throw new Error("Unable to determine encType."); + } + } + + this.key = key; + this.encType = encType; + + if (encType === EncryptionType.AesCbc256_B64 && key.byteLength === 32) { + this.encKey = key; + this.macKey = null; + } else if (encType === EncryptionType.AesCbc128_HmacSha256_B64 && key.byteLength === 32) { + this.encKey = key.slice(0, 16); + this.macKey = key.slice(16, 32); + } else if (encType === EncryptionType.AesCbc256_HmacSha256_B64 && key.byteLength === 64) { + this.encKey = key.slice(0, 32); + this.macKey = key.slice(32, 64); + } else { + throw new Error("Unsupported encType/key length."); + } + + if (this.key != null) { + this.keyB64 = Utils.fromBufferToB64(this.key); + } + if (this.encKey != null) { + this.encKeyB64 = Utils.fromBufferToB64(this.encKey); + } + if (this.macKey != null) { + this.macKeyB64 = Utils.fromBufferToB64(this.macKey); + } + } +} diff --git a/jslib/common/src/models/domain/treeNode.ts b/jslib/common/src/models/domain/treeNode.ts new file mode 100644 index 00000000..6af973a5 --- /dev/null +++ b/jslib/common/src/models/domain/treeNode.ts @@ -0,0 +1,16 @@ +export class TreeNode { + parent: T; + node: T; + children: TreeNode[] = []; + + constructor(node: T, name: string, parent: T) { + this.parent = parent; + this.node = node; + this.node.name = name; + } +} + +export interface ITreeNodeObject { + id: string; + name: string; +} diff --git a/jslib/common/src/models/domain/windowState.ts b/jslib/common/src/models/domain/windowState.ts new file mode 100644 index 00000000..cb260d8b --- /dev/null +++ b/jslib/common/src/models/domain/windowState.ts @@ -0,0 +1,10 @@ +export class WindowState { + width?: number; + height?: number; + isMaximized?: boolean; + // TODO: displayBounds is an Electron.Rectangle. + // We need to establish some kind of client-specific global state, similiar to the way we already extend a base Account. + displayBounds: any; + x?: number; + y?: number; +} diff --git a/jslib/common/src/models/export/card.ts b/jslib/common/src/models/export/card.ts new file mode 100644 index 00000000..b320ec8b --- /dev/null +++ b/jslib/common/src/models/export/card.ts @@ -0,0 +1,65 @@ +import { Card as CardDomain } from "../domain/card"; +import { EncString } from "../domain/encString"; +import { CardView } from "../view/cardView"; + +export class Card { + static template(): Card { + const req = new Card(); + req.cardholderName = "John Doe"; + req.brand = "visa"; + req.number = "4242424242424242"; + req.expMonth = "04"; + req.expYear = "2023"; + req.code = "123"; + return req; + } + + static toView(req: Card, view = new CardView()) { + view.cardholderName = req.cardholderName; + view.brand = req.brand; + view.number = req.number; + view.expMonth = req.expMonth; + view.expYear = req.expYear; + view.code = req.code; + return view; + } + + static toDomain(req: Card, domain = new CardDomain()) { + domain.cardholderName = req.cardholderName != null ? new EncString(req.cardholderName) : null; + domain.brand = req.brand != null ? new EncString(req.brand) : null; + domain.number = req.number != null ? new EncString(req.number) : null; + domain.expMonth = req.expMonth != null ? new EncString(req.expMonth) : null; + domain.expYear = req.expYear != null ? new EncString(req.expYear) : null; + domain.code = req.code != null ? new EncString(req.code) : null; + return domain; + } + + cardholderName: string; + brand: string; + number: string; + expMonth: string; + expYear: string; + code: string; + + constructor(o?: CardView | CardDomain) { + if (o == null) { + return; + } + + if (o instanceof CardView) { + this.cardholderName = o.cardholderName; + this.brand = o.brand; + this.number = o.number; + this.expMonth = o.expMonth; + this.expYear = o.expYear; + this.code = o.code; + } else { + this.cardholderName = o.cardholderName?.encryptedString; + this.brand = o.brand?.encryptedString; + this.number = o.number?.encryptedString; + this.expMonth = o.expMonth?.encryptedString; + this.expYear = o.expYear?.encryptedString; + this.code = o.code?.encryptedString; + } + } +} diff --git a/jslib/common/src/models/export/cipher.ts b/jslib/common/src/models/export/cipher.ts new file mode 100644 index 00000000..5d650666 --- /dev/null +++ b/jslib/common/src/models/export/cipher.ts @@ -0,0 +1,156 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { Cipher as CipherDomain } from "../domain/cipher"; +import { EncString } from "../domain/encString"; +import { CipherView } from "../view/cipherView"; + +import { Card } from "./card"; +import { Field } from "./field"; +import { Identity } from "./identity"; +import { Login } from "./login"; +import { SecureNote } from "./secureNote"; + +export class Cipher { + static template(): Cipher { + const req = new Cipher(); + req.organizationId = null; + req.collectionIds = null; + req.folderId = null; + req.type = CipherType.Login; + req.name = "Item name"; + req.notes = "Some notes about this item."; + req.favorite = false; + req.fields = []; + req.login = null; + req.secureNote = null; + req.card = null; + req.identity = null; + req.reprompt = CipherRepromptType.None; + return req; + } + + static toView(req: Cipher, view = new CipherView()) { + view.type = req.type; + view.folderId = req.folderId; + if (view.organizationId == null) { + view.organizationId = req.organizationId; + } + if (view.collectionIds || req.collectionIds) { + const set = new Set((view.collectionIds ?? []).concat(req.collectionIds ?? [])); + view.collectionIds = Array.from(set.values()); + } + view.name = req.name; + view.notes = req.notes; + view.favorite = req.favorite; + view.reprompt = req.reprompt ?? CipherRepromptType.None; + + if (req.fields != null) { + view.fields = req.fields.map((f) => Field.toView(f)); + } + + switch (req.type) { + case CipherType.Login: + view.login = Login.toView(req.login); + break; + case CipherType.SecureNote: + view.secureNote = SecureNote.toView(req.secureNote); + break; + case CipherType.Card: + view.card = Card.toView(req.card); + break; + case CipherType.Identity: + view.identity = Identity.toView(req.identity); + break; + } + + return view; + } + + static toDomain(req: Cipher, domain = new CipherDomain()) { + domain.type = req.type; + domain.folderId = req.folderId; + if (domain.organizationId == null) { + domain.organizationId = req.organizationId; + } + domain.name = req.name != null ? new EncString(req.name) : null; + domain.notes = req.notes != null ? new EncString(req.notes) : null; + domain.favorite = req.favorite; + domain.reprompt = req.reprompt ?? CipherRepromptType.None; + + if (req.fields != null) { + domain.fields = req.fields.map((f) => Field.toDomain(f)); + } + + switch (req.type) { + case CipherType.Login: + domain.login = Login.toDomain(req.login); + break; + case CipherType.SecureNote: + domain.secureNote = SecureNote.toDomain(req.secureNote); + break; + case CipherType.Card: + domain.card = Card.toDomain(req.card); + break; + case CipherType.Identity: + domain.identity = Identity.toDomain(req.identity); + break; + } + + return domain; + } + + type: CipherType; + folderId: string; + organizationId: string; + collectionIds: string[]; + name: string; + notes: string; + favorite: boolean; + fields: Field[]; + login: Login; + secureNote: SecureNote; + card: Card; + identity: Identity; + reprompt: CipherRepromptType; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: CipherView | CipherDomain) { + this.organizationId = o.organizationId; + this.folderId = o.folderId; + this.type = o.type; + this.reprompt = o.reprompt; + + if (o instanceof CipherView) { + this.name = o.name; + this.notes = o.notes; + } else { + this.name = o.name?.encryptedString; + this.notes = o.notes?.encryptedString; + } + + this.favorite = o.favorite; + + if (o.fields != null) { + if (o instanceof CipherView) { + this.fields = o.fields.map((f) => new Field(f)); + } else { + this.fields = o.fields.map((f) => new Field(f)); + } + } + + switch (o.type) { + case CipherType.Login: + this.login = new Login(o.login); + break; + case CipherType.SecureNote: + this.secureNote = new SecureNote(o.secureNote); + break; + case CipherType.Card: + this.card = new Card(o.card); + break; + case CipherType.Identity: + this.identity = new Identity(o.identity); + break; + } + } +} diff --git a/jslib/common/src/models/export/cipherWithIds.ts b/jslib/common/src/models/export/cipherWithIds.ts new file mode 100644 index 00000000..a5698c6d --- /dev/null +++ b/jslib/common/src/models/export/cipherWithIds.ts @@ -0,0 +1,16 @@ +import { Cipher as CipherDomain } from "../domain/cipher"; +import { CipherView } from "../view/cipherView"; + +import { Cipher } from "./cipher"; + +export class CipherWithIds extends Cipher { + id: string; + collectionIds: string[]; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: CipherView | CipherDomain) { + this.id = o.id; + super.build(o); + this.collectionIds = o.collectionIds; + } +} diff --git a/jslib/common/src/models/export/collection.ts b/jslib/common/src/models/export/collection.ts new file mode 100644 index 00000000..fba9af15 --- /dev/null +++ b/jslib/common/src/models/export/collection.ts @@ -0,0 +1,46 @@ +import { Collection as CollectionDomain } from "../domain/collection"; +import { EncString } from "../domain/encString"; +import { CollectionView } from "../view/collectionView"; + +export class Collection { + static template(): Collection { + const req = new Collection(); + req.organizationId = "00000000-0000-0000-0000-000000000000"; + req.name = "Collection name"; + req.externalId = null; + return req; + } + + static toView(req: Collection, view = new CollectionView()) { + view.name = req.name; + view.externalId = req.externalId; + if (view.organizationId == null) { + view.organizationId = req.organizationId; + } + return view; + } + + static toDomain(req: Collection, domain = new CollectionDomain()) { + domain.name = req.name != null ? new EncString(req.name) : null; + domain.externalId = req.externalId; + if (domain.organizationId == null) { + domain.organizationId = req.organizationId; + } + return domain; + } + + organizationId: string; + name: string; + externalId: string; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: CollectionView | CollectionDomain) { + this.organizationId = o.organizationId; + if (o instanceof CollectionView) { + this.name = o.name; + } else { + this.name = o.name?.encryptedString; + } + this.externalId = o.externalId; + } +} diff --git a/jslib/common/src/models/export/collectionWithId.ts b/jslib/common/src/models/export/collectionWithId.ts new file mode 100644 index 00000000..7bc0707a --- /dev/null +++ b/jslib/common/src/models/export/collectionWithId.ts @@ -0,0 +1,14 @@ +import { Collection as CollectionDomain } from "../domain/collection"; +import { CollectionView } from "../view/collectionView"; + +import { Collection } from "./collection"; + +export class CollectionWithId extends Collection { + id: string; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: CollectionView | CollectionDomain) { + this.id = o.id; + super.build(o); + } +} diff --git a/jslib/common/src/models/export/event.ts b/jslib/common/src/models/export/event.ts new file mode 100644 index 00000000..09975f2f --- /dev/null +++ b/jslib/common/src/models/export/event.ts @@ -0,0 +1,26 @@ +import { EventType } from "../../enums/eventType"; +import { EventView } from "../view/eventView"; + +export class Event { + message: string; + appIcon: string; + appName: string; + userId: string; + userName: string; + userEmail: string; + date: string; + ip: string; + type: string; + + constructor(event: EventView) { + this.message = event.humanReadableMessage; + this.appIcon = event.appIcon; + this.appName = event.appName; + this.userId = event.userId; + this.userName = event.userName; + this.userEmail = event.userEmail; + this.date = event.date; + this.ip = event.ip; + this.type = EventType[event.type]; + } +} diff --git a/jslib/common/src/models/export/field.ts b/jslib/common/src/models/export/field.ts new file mode 100644 index 00000000..c8ce7581 --- /dev/null +++ b/jslib/common/src/models/export/field.ts @@ -0,0 +1,52 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { EncString } from "../domain/encString"; +import { Field as FieldDomain } from "../domain/field"; +import { FieldView } from "../view/fieldView"; + +export class Field { + static template(): Field { + const req = new Field(); + req.name = "Field name"; + req.value = "Some value"; + req.type = FieldType.Text; + return req; + } + + static toView(req: Field, view = new FieldView()) { + view.type = req.type; + view.value = req.value; + view.name = req.name; + view.linkedId = req.linkedId; + return view; + } + + static toDomain(req: Field, domain = new FieldDomain()) { + domain.type = req.type; + domain.value = req.value != null ? new EncString(req.value) : null; + domain.name = req.name != null ? new EncString(req.name) : null; + domain.linkedId = req.linkedId; + return domain; + } + + name: string; + value: string; + type: FieldType; + linkedId: LinkedIdType; + + constructor(o?: FieldView | FieldDomain) { + if (o == null) { + return; + } + + if (o instanceof FieldView) { + this.name = o.name; + this.value = o.value; + } else { + this.name = o.name?.encryptedString; + this.value = o.value?.encryptedString; + } + this.type = o.type; + this.linkedId = o.linkedId; + } +} diff --git a/jslib/common/src/models/export/folder.ts b/jslib/common/src/models/export/folder.ts new file mode 100644 index 00000000..e770f326 --- /dev/null +++ b/jslib/common/src/models/export/folder.ts @@ -0,0 +1,32 @@ +import { EncString } from "../domain/encString"; +import { Folder as FolderDomain } from "../domain/folder"; +import { FolderView } from "../view/folderView"; + +export class Folder { + static template(): Folder { + const req = new Folder(); + req.name = "Folder name"; + return req; + } + + static toView(req: Folder, view = new FolderView()) { + view.name = req.name; + return view; + } + + static toDomain(req: Folder, domain = new FolderDomain()) { + domain.name = req.name != null ? new EncString(req.name) : null; + return domain; + } + + name: string; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: FolderView | FolderDomain) { + if (o instanceof FolderView) { + this.name = o.name; + } else { + this.name = o.name?.encryptedString; + } + } +} diff --git a/jslib/common/src/models/export/folderWithId.ts b/jslib/common/src/models/export/folderWithId.ts new file mode 100644 index 00000000..a5507d51 --- /dev/null +++ b/jslib/common/src/models/export/folderWithId.ts @@ -0,0 +1,14 @@ +import { Folder as FolderDomain } from "../domain/folder"; +import { FolderView } from "../view/folderView"; + +import { Folder } from "./folder"; + +export class FolderWithId extends Folder { + id: string; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: FolderView | FolderDomain) { + this.id = o.id; + super.build(o); + } +} diff --git a/jslib/common/src/models/export/identity.ts b/jslib/common/src/models/export/identity.ts new file mode 100644 index 00000000..144732db --- /dev/null +++ b/jslib/common/src/models/export/identity.ts @@ -0,0 +1,137 @@ +import { EncString } from "../domain/encString"; +import { Identity as IdentityDomain } from "../domain/identity"; +import { IdentityView } from "../view/identityView"; + +export class Identity { + static template(): Identity { + const req = new Identity(); + req.title = "Mr"; + req.firstName = "John"; + req.middleName = "William"; + req.lastName = "Doe"; + req.address1 = "123 Any St"; + req.address2 = "Apt #123"; + req.address3 = null; + req.city = "New York"; + req.state = "NY"; + req.postalCode = "10001"; + req.country = "US"; + req.company = "Acme Inc."; + req.email = "john@company.com"; + req.phone = "5555551234"; + req.ssn = "000-123-4567"; + req.username = "jdoe"; + req.passportNumber = "US-123456789"; + req.licenseNumber = "D123-12-123-12333"; + return req; + } + + static toView(req: Identity, view = new IdentityView()) { + view.title = req.title; + view.firstName = req.firstName; + view.middleName = req.middleName; + view.lastName = req.lastName; + view.address1 = req.address1; + view.address2 = req.address2; + view.address3 = req.address3; + view.city = req.city; + view.state = req.state; + view.postalCode = req.postalCode; + view.country = req.country; + view.company = req.company; + view.email = req.email; + view.phone = req.phone; + view.ssn = req.ssn; + view.username = req.username; + view.passportNumber = req.passportNumber; + view.licenseNumber = req.licenseNumber; + return view; + } + + static toDomain(req: Identity, domain = new IdentityDomain()) { + domain.title = req.title != null ? new EncString(req.title) : null; + domain.firstName = req.firstName != null ? new EncString(req.firstName) : null; + domain.middleName = req.middleName != null ? new EncString(req.middleName) : null; + domain.lastName = req.lastName != null ? new EncString(req.lastName) : null; + domain.address1 = req.address1 != null ? new EncString(req.address1) : null; + domain.address2 = req.address2 != null ? new EncString(req.address2) : null; + domain.address3 = req.address3 != null ? new EncString(req.address3) : null; + domain.city = req.city != null ? new EncString(req.city) : null; + domain.state = req.state != null ? new EncString(req.state) : null; + domain.postalCode = req.postalCode != null ? new EncString(req.postalCode) : null; + domain.country = req.country != null ? new EncString(req.country) : null; + domain.company = req.company != null ? new EncString(req.company) : null; + domain.email = req.email != null ? new EncString(req.email) : null; + domain.phone = req.phone != null ? new EncString(req.phone) : null; + domain.ssn = req.ssn != null ? new EncString(req.ssn) : null; + domain.username = req.username != null ? new EncString(req.username) : null; + domain.passportNumber = req.passportNumber != null ? new EncString(req.passportNumber) : null; + domain.licenseNumber = req.licenseNumber != null ? new EncString(req.licenseNumber) : null; + return domain; + } + + title: string; + firstName: string; + middleName: string; + lastName: string; + address1: string; + address2: string; + address3: string; + city: string; + state: string; + postalCode: string; + country: string; + company: string; + email: string; + phone: string; + ssn: string; + username: string; + passportNumber: string; + licenseNumber: string; + + constructor(o?: IdentityView | IdentityDomain) { + if (o == null) { + return; + } + + if (o instanceof IdentityView) { + this.title = o.title; + this.firstName = o.firstName; + this.middleName = o.middleName; + this.lastName = o.lastName; + this.address1 = o.address1; + this.address2 = o.address2; + this.address3 = o.address3; + this.city = o.city; + this.state = o.state; + this.postalCode = o.postalCode; + this.country = o.country; + this.company = o.company; + this.email = o.email; + this.phone = o.phone; + this.ssn = o.ssn; + this.username = o.username; + this.passportNumber = o.passportNumber; + this.licenseNumber = o.licenseNumber; + } else { + this.title = o.title?.encryptedString; + this.firstName = o.firstName?.encryptedString; + this.middleName = o.middleName?.encryptedString; + this.lastName = o.lastName?.encryptedString; + this.address1 = o.address1?.encryptedString; + this.address2 = o.address2?.encryptedString; + this.address3 = o.address3?.encryptedString; + this.city = o.city?.encryptedString; + this.state = o.state?.encryptedString; + this.postalCode = o.postalCode?.encryptedString; + this.country = o.country?.encryptedString; + this.company = o.company?.encryptedString; + this.email = o.email?.encryptedString; + this.phone = o.phone?.encryptedString; + this.ssn = o.ssn?.encryptedString; + this.username = o.username?.encryptedString; + this.passportNumber = o.passportNumber?.encryptedString; + this.licenseNumber = o.licenseNumber?.encryptedString; + } + } +} diff --git a/jslib/common/src/models/export/login.ts b/jslib/common/src/models/export/login.ts new file mode 100644 index 00000000..6a07ff74 --- /dev/null +++ b/jslib/common/src/models/export/login.ts @@ -0,0 +1,65 @@ +import { EncString } from "../domain/encString"; +import { Login as LoginDomain } from "../domain/login"; +import { LoginView } from "../view/loginView"; + +import { LoginUri } from "./loginUri"; + +export class Login { + static template(): Login { + const req = new Login(); + req.uris = []; + req.username = "jdoe"; + req.password = "myp@ssword123"; + req.totp = "JBSWY3DPEHPK3PXP"; + return req; + } + + static toView(req: Login, view = new LoginView()) { + if (req.uris != null) { + view.uris = req.uris.map((u) => LoginUri.toView(u)); + } + view.username = req.username; + view.password = req.password; + view.totp = req.totp; + return view; + } + + static toDomain(req: Login, domain = new LoginDomain()) { + if (req.uris != null) { + domain.uris = req.uris.map((u) => LoginUri.toDomain(u)); + } + domain.username = req.username != null ? new EncString(req.username) : null; + domain.password = req.password != null ? new EncString(req.password) : null; + domain.totp = req.totp != null ? new EncString(req.totp) : null; + return domain; + } + + uris: LoginUri[]; + username: string; + password: string; + totp: string; + + constructor(o?: LoginView | LoginDomain) { + if (o == null) { + return; + } + + if (o.uris != null) { + if (o instanceof LoginView) { + this.uris = o.uris.map((u) => new LoginUri(u)); + } else { + this.uris = o.uris.map((u) => new LoginUri(u)); + } + } + + if (o instanceof LoginView) { + this.username = o.username; + this.password = o.password; + this.totp = o.totp; + } else { + this.username = o.username?.encryptedString; + this.password = o.password?.encryptedString; + this.totp = o.totp?.encryptedString; + } + } +} diff --git a/jslib/common/src/models/export/loginUri.ts b/jslib/common/src/models/export/loginUri.ts new file mode 100644 index 00000000..3e6d588b --- /dev/null +++ b/jslib/common/src/models/export/loginUri.ts @@ -0,0 +1,41 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { EncString } from "../domain/encString"; +import { LoginUri as LoginUriDomain } from "../domain/loginUri"; +import { LoginUriView } from "../view/loginUriView"; + +export class LoginUri { + static template(): LoginUri { + const req = new LoginUri(); + req.uri = "https://google.com"; + req.match = null; + return req; + } + + static toView(req: LoginUri, view = new LoginUriView()) { + view.uri = req.uri; + view.match = req.match; + return view; + } + + static toDomain(req: LoginUri, domain = new LoginUriDomain()) { + domain.uri = req.uri != null ? new EncString(req.uri) : null; + domain.match = req.match; + return domain; + } + + uri: string; + match: UriMatchType = null; + + constructor(o?: LoginUriView | LoginUriDomain) { + if (o == null) { + return; + } + + if (o instanceof LoginUriView) { + this.uri = o.uri; + } else { + this.uri = o.uri?.encryptedString; + } + this.match = o.match; + } +} diff --git a/jslib/common/src/models/export/secureNote.ts b/jslib/common/src/models/export/secureNote.ts new file mode 100644 index 00000000..851ba8e4 --- /dev/null +++ b/jslib/common/src/models/export/secureNote.ts @@ -0,0 +1,31 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { SecureNote as SecureNoteDomain } from "../domain/secureNote"; +import { SecureNoteView } from "../view/secureNoteView"; + +export class SecureNote { + static template(): SecureNote { + const req = new SecureNote(); + req.type = SecureNoteType.Generic; + return req; + } + + static toView(req: SecureNote, view = new SecureNoteView()) { + view.type = req.type; + return view; + } + + static toDomain(req: SecureNote, view = new SecureNoteDomain()) { + view.type = req.type; + return view; + } + + type: SecureNoteType; + + constructor(o?: SecureNoteView | SecureNoteDomain) { + if (o == null) { + return; + } + + this.type = o.type; + } +} diff --git a/jslib/common/src/models/request/account/setKeyConnectorKeyRequest.ts b/jslib/common/src/models/request/account/setKeyConnectorKeyRequest.ts new file mode 100644 index 00000000..a84cc7ef --- /dev/null +++ b/jslib/common/src/models/request/account/setKeyConnectorKeyRequest.ts @@ -0,0 +1,24 @@ +import { KdfType } from "../../../enums/kdfType"; +import { KeysRequest } from "../keysRequest"; + +export class SetKeyConnectorKeyRequest { + key: string; + keys: KeysRequest; + kdf: KdfType; + kdfIterations: number; + orgIdentifier: string; + + constructor( + key: string, + kdf: KdfType, + kdfIterations: number, + orgIdentifier: string, + keys: KeysRequest + ) { + this.key = key; + this.kdf = kdf; + this.kdfIterations = kdfIterations; + this.orgIdentifier = orgIdentifier; + this.keys = keys; + } +} diff --git a/jslib/common/src/models/request/account/verifyOTPRequest.ts b/jslib/common/src/models/request/account/verifyOTPRequest.ts new file mode 100644 index 00000000..2eb8816e --- /dev/null +++ b/jslib/common/src/models/request/account/verifyOTPRequest.ts @@ -0,0 +1,7 @@ +export class VerifyOTPRequest { + OTP: string; + + constructor(OTP: string) { + this.OTP = OTP; + } +} diff --git a/jslib/common/src/models/request/attachmentRequest.ts b/jslib/common/src/models/request/attachmentRequest.ts new file mode 100644 index 00000000..ea1ea821 --- /dev/null +++ b/jslib/common/src/models/request/attachmentRequest.ts @@ -0,0 +1,6 @@ +export class AttachmentRequest { + fileName: string; + key: string; + fileSize: number; + adminRequest: boolean; +} diff --git a/jslib/common/src/models/request/bitPayInvoiceRequest.ts b/jslib/common/src/models/request/bitPayInvoiceRequest.ts new file mode 100644 index 00000000..9042611b --- /dev/null +++ b/jslib/common/src/models/request/bitPayInvoiceRequest.ts @@ -0,0 +1,9 @@ +export class BitPayInvoiceRequest { + userId: string; + organizationId: string; + credit: boolean; + amount: number; + returnUrl: string; + name: string; + email: string; +} diff --git a/jslib/common/src/models/request/captchaProtectedRequest.ts b/jslib/common/src/models/request/captchaProtectedRequest.ts new file mode 100644 index 00000000..54721e01 --- /dev/null +++ b/jslib/common/src/models/request/captchaProtectedRequest.ts @@ -0,0 +1,3 @@ +export abstract class CaptchaProtectedRequest { + captchaResponse: string = null; +} diff --git a/jslib/common/src/models/request/cipherBulkDeleteRequest.ts b/jslib/common/src/models/request/cipherBulkDeleteRequest.ts new file mode 100644 index 00000000..227f1a66 --- /dev/null +++ b/jslib/common/src/models/request/cipherBulkDeleteRequest.ts @@ -0,0 +1,9 @@ +export class CipherBulkDeleteRequest { + ids: string[]; + organizationId: string; + + constructor(ids: string[], organizationId?: string) { + this.ids = ids == null ? [] : ids; + this.organizationId = organizationId; + } +} diff --git a/jslib/common/src/models/request/cipherBulkMoveRequest.ts b/jslib/common/src/models/request/cipherBulkMoveRequest.ts new file mode 100644 index 00000000..06a737de --- /dev/null +++ b/jslib/common/src/models/request/cipherBulkMoveRequest.ts @@ -0,0 +1,9 @@ +export class CipherBulkMoveRequest { + ids: string[]; + folderId: string; + + constructor(ids: string[], folderId: string) { + this.ids = ids == null ? [] : ids; + this.folderId = folderId; + } +} diff --git a/jslib/common/src/models/request/cipherBulkRestoreRequest.ts b/jslib/common/src/models/request/cipherBulkRestoreRequest.ts new file mode 100644 index 00000000..70e5a4e8 --- /dev/null +++ b/jslib/common/src/models/request/cipherBulkRestoreRequest.ts @@ -0,0 +1,7 @@ +export class CipherBulkRestoreRequest { + ids: string[]; + + constructor(ids: string[]) { + this.ids = ids == null ? [] : ids; + } +} diff --git a/jslib/common/src/models/request/cipherBulkShareRequest.ts b/jslib/common/src/models/request/cipherBulkShareRequest.ts new file mode 100644 index 00000000..6e77ad56 --- /dev/null +++ b/jslib/common/src/models/request/cipherBulkShareRequest.ts @@ -0,0 +1,18 @@ +import { Cipher } from "../domain/cipher"; + +import { CipherWithIdRequest } from "./cipherWithIdRequest"; + +export class CipherBulkShareRequest { + ciphers: CipherWithIdRequest[]; + collectionIds: string[]; + + constructor(ciphers: Cipher[], collectionIds: string[]) { + if (ciphers != null) { + this.ciphers = []; + ciphers.forEach((c) => { + this.ciphers.push(new CipherWithIdRequest(c)); + }); + } + this.collectionIds = collectionIds; + } +} diff --git a/jslib/common/src/models/request/cipherCollectionsRequest.ts b/jslib/common/src/models/request/cipherCollectionsRequest.ts new file mode 100644 index 00000000..8d555389 --- /dev/null +++ b/jslib/common/src/models/request/cipherCollectionsRequest.ts @@ -0,0 +1,7 @@ +export class CipherCollectionsRequest { + collectionIds: string[]; + + constructor(collectionIds: string[]) { + this.collectionIds = collectionIds == null ? [] : collectionIds; + } +} diff --git a/jslib/common/src/models/request/cipherCreateRequest.ts b/jslib/common/src/models/request/cipherCreateRequest.ts new file mode 100644 index 00000000..d7ff5128 --- /dev/null +++ b/jslib/common/src/models/request/cipherCreateRequest.ts @@ -0,0 +1,13 @@ +import { Cipher } from "../domain/cipher"; + +import { CipherRequest } from "./cipherRequest"; + +export class CipherCreateRequest { + cipher: CipherRequest; + collectionIds: string[]; + + constructor(cipher: Cipher) { + this.cipher = new CipherRequest(cipher); + this.collectionIds = cipher.collectionIds; + } +} diff --git a/jslib/common/src/models/request/cipherRequest.ts b/jslib/common/src/models/request/cipherRequest.ts new file mode 100644 index 00000000..4ed58850 --- /dev/null +++ b/jslib/common/src/models/request/cipherRequest.ts @@ -0,0 +1,164 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { CardApi } from "../api/cardApi"; +import { FieldApi } from "../api/fieldApi"; +import { IdentityApi } from "../api/identityApi"; +import { LoginApi } from "../api/loginApi"; +import { LoginUriApi } from "../api/loginUriApi"; +import { SecureNoteApi } from "../api/secureNoteApi"; +import { Cipher } from "../domain/cipher"; + +import { AttachmentRequest } from "./attachmentRequest"; +import { PasswordHistoryRequest } from "./passwordHistoryRequest"; + +export class CipherRequest { + type: CipherType; + folderId: string; + organizationId: string; + name: string; + notes: string; + favorite: boolean; + login: LoginApi; + secureNote: SecureNoteApi; + card: CardApi; + identity: IdentityApi; + fields: FieldApi[]; + passwordHistory: PasswordHistoryRequest[]; + // Deprecated, remove at some point and rename attachments2 to attachments + attachments: { [id: string]: string }; + attachments2: { [id: string]: AttachmentRequest }; + lastKnownRevisionDate: Date; + reprompt: CipherRepromptType; + + constructor(cipher: Cipher) { + this.type = cipher.type; + this.folderId = cipher.folderId; + this.organizationId = cipher.organizationId; + this.name = cipher.name ? cipher.name.encryptedString : null; + this.notes = cipher.notes ? cipher.notes.encryptedString : null; + this.favorite = cipher.favorite; + this.lastKnownRevisionDate = cipher.revisionDate; + this.reprompt = cipher.reprompt; + + switch (this.type) { + case CipherType.Login: + this.login = new LoginApi(); + this.login.uris = null; + this.login.username = cipher.login.username ? cipher.login.username.encryptedString : null; + this.login.password = cipher.login.password ? cipher.login.password.encryptedString : null; + this.login.passwordRevisionDate = + cipher.login.passwordRevisionDate != null + ? cipher.login.passwordRevisionDate.toISOString() + : null; + this.login.totp = cipher.login.totp ? cipher.login.totp.encryptedString : null; + this.login.autofillOnPageLoad = cipher.login.autofillOnPageLoad; + + if (cipher.login.uris != null) { + this.login.uris = cipher.login.uris.map((u) => { + const uri = new LoginUriApi(); + uri.uri = u.uri != null ? u.uri.encryptedString : null; + uri.match = u.match != null ? u.match : null; + return uri; + }); + } + break; + case CipherType.SecureNote: + this.secureNote = new SecureNoteApi(); + this.secureNote.type = cipher.secureNote.type; + break; + case CipherType.Card: + this.card = new CardApi(); + this.card.cardholderName = + cipher.card.cardholderName != null ? cipher.card.cardholderName.encryptedString : null; + this.card.brand = cipher.card.brand != null ? cipher.card.brand.encryptedString : null; + this.card.number = cipher.card.number != null ? cipher.card.number.encryptedString : null; + this.card.expMonth = + cipher.card.expMonth != null ? cipher.card.expMonth.encryptedString : null; + this.card.expYear = + cipher.card.expYear != null ? cipher.card.expYear.encryptedString : null; + this.card.code = cipher.card.code != null ? cipher.card.code.encryptedString : null; + break; + case CipherType.Identity: + this.identity = new IdentityApi(); + this.identity.title = + cipher.identity.title != null ? cipher.identity.title.encryptedString : null; + this.identity.firstName = + cipher.identity.firstName != null ? cipher.identity.firstName.encryptedString : null; + this.identity.middleName = + cipher.identity.middleName != null ? cipher.identity.middleName.encryptedString : null; + this.identity.lastName = + cipher.identity.lastName != null ? cipher.identity.lastName.encryptedString : null; + this.identity.address1 = + cipher.identity.address1 != null ? cipher.identity.address1.encryptedString : null; + this.identity.address2 = + cipher.identity.address2 != null ? cipher.identity.address2.encryptedString : null; + this.identity.address3 = + cipher.identity.address3 != null ? cipher.identity.address3.encryptedString : null; + this.identity.city = + cipher.identity.city != null ? cipher.identity.city.encryptedString : null; + this.identity.state = + cipher.identity.state != null ? cipher.identity.state.encryptedString : null; + this.identity.postalCode = + cipher.identity.postalCode != null ? cipher.identity.postalCode.encryptedString : null; + this.identity.country = + cipher.identity.country != null ? cipher.identity.country.encryptedString : null; + this.identity.company = + cipher.identity.company != null ? cipher.identity.company.encryptedString : null; + this.identity.email = + cipher.identity.email != null ? cipher.identity.email.encryptedString : null; + this.identity.phone = + cipher.identity.phone != null ? cipher.identity.phone.encryptedString : null; + this.identity.ssn = + cipher.identity.ssn != null ? cipher.identity.ssn.encryptedString : null; + this.identity.username = + cipher.identity.username != null ? cipher.identity.username.encryptedString : null; + this.identity.passportNumber = + cipher.identity.passportNumber != null + ? cipher.identity.passportNumber.encryptedString + : null; + this.identity.licenseNumber = + cipher.identity.licenseNumber != null + ? cipher.identity.licenseNumber.encryptedString + : null; + break; + default: + break; + } + + if (cipher.fields != null) { + this.fields = cipher.fields.map((f) => { + const field = new FieldApi(); + field.type = f.type; + field.name = f.name ? f.name.encryptedString : null; + field.value = f.value ? f.value.encryptedString : null; + field.linkedId = f.linkedId; + return field; + }); + } + + if (cipher.passwordHistory != null) { + this.passwordHistory = []; + cipher.passwordHistory.forEach((ph) => { + this.passwordHistory.push({ + lastUsedDate: ph.lastUsedDate, + password: ph.password ? ph.password.encryptedString : null, + }); + }); + } + + if (cipher.attachments != null) { + this.attachments = {}; + this.attachments2 = {}; + cipher.attachments.forEach((attachment) => { + const fileName = attachment.fileName ? attachment.fileName.encryptedString : null; + this.attachments[attachment.id] = fileName; + const attachmentRequest = new AttachmentRequest(); + attachmentRequest.fileName = fileName; + if (attachment.key != null) { + attachmentRequest.key = attachment.key.encryptedString; + } + this.attachments2[attachment.id] = attachmentRequest; + }); + } + } +} diff --git a/jslib/common/src/models/request/cipherShareRequest.ts b/jslib/common/src/models/request/cipherShareRequest.ts new file mode 100644 index 00000000..4cf6dfde --- /dev/null +++ b/jslib/common/src/models/request/cipherShareRequest.ts @@ -0,0 +1,13 @@ +import { Cipher } from "../domain/cipher"; + +import { CipherRequest } from "./cipherRequest"; + +export class CipherShareRequest { + cipher: CipherRequest; + collectionIds: string[]; + + constructor(cipher: Cipher) { + this.cipher = new CipherRequest(cipher); + this.collectionIds = cipher.collectionIds; + } +} diff --git a/jslib/common/src/models/request/cipherWithIdRequest.ts b/jslib/common/src/models/request/cipherWithIdRequest.ts new file mode 100644 index 00000000..0f22322f --- /dev/null +++ b/jslib/common/src/models/request/cipherWithIdRequest.ts @@ -0,0 +1,12 @@ +import { Cipher } from "../domain/cipher"; + +import { CipherRequest } from "./cipherRequest"; + +export class CipherWithIdRequest extends CipherRequest { + id: string; + + constructor(cipher: Cipher) { + super(cipher); + this.id = cipher.id; + } +} diff --git a/jslib/common/src/models/request/collectionRequest.ts b/jslib/common/src/models/request/collectionRequest.ts new file mode 100644 index 00000000..c2176095 --- /dev/null +++ b/jslib/common/src/models/request/collectionRequest.ts @@ -0,0 +1,17 @@ +import { Collection } from "../domain/collection"; + +import { SelectionReadOnlyRequest } from "./selectionReadOnlyRequest"; + +export class CollectionRequest { + name: string; + externalId: string; + groups: SelectionReadOnlyRequest[] = []; + + constructor(collection?: Collection) { + if (collection == null) { + return; + } + this.name = collection.name ? collection.name.encryptedString : null; + this.externalId = collection.externalId; + } +} diff --git a/jslib/common/src/models/request/deleteRecoverRequest.ts b/jslib/common/src/models/request/deleteRecoverRequest.ts new file mode 100644 index 00000000..02a019b8 --- /dev/null +++ b/jslib/common/src/models/request/deleteRecoverRequest.ts @@ -0,0 +1,3 @@ +export class DeleteRecoverRequest { + email: string; +} diff --git a/jslib/common/src/models/request/deviceRequest.ts b/jslib/common/src/models/request/deviceRequest.ts new file mode 100644 index 00000000..66c17b57 --- /dev/null +++ b/jslib/common/src/models/request/deviceRequest.ts @@ -0,0 +1,16 @@ +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { DeviceType } from "../../enums/deviceType"; + +export class DeviceRequest { + type: DeviceType; + name: string; + identifier: string; + pushToken?: string; + + constructor(appId: string, platformUtilsService: PlatformUtilsService) { + this.type = platformUtilsService.getDevice(); + this.name = platformUtilsService.getDeviceString(); + this.identifier = appId; + this.pushToken = null; + } +} diff --git a/jslib/common/src/models/request/deviceTokenRequest.ts b/jslib/common/src/models/request/deviceTokenRequest.ts new file mode 100644 index 00000000..99ca69a2 --- /dev/null +++ b/jslib/common/src/models/request/deviceTokenRequest.ts @@ -0,0 +1,7 @@ +export class DeviceTokenRequest { + pushToken: string; + + constructor() { + this.pushToken = null; + } +} diff --git a/jslib/common/src/models/request/emailRequest.ts b/jslib/common/src/models/request/emailRequest.ts new file mode 100644 index 00000000..0b559615 --- /dev/null +++ b/jslib/common/src/models/request/emailRequest.ts @@ -0,0 +1,7 @@ +import { EmailTokenRequest } from "./emailTokenRequest"; + +export class EmailRequest extends EmailTokenRequest { + newMasterPasswordHash: string; + token: string; + key: string; +} diff --git a/jslib/common/src/models/request/emailTokenRequest.ts b/jslib/common/src/models/request/emailTokenRequest.ts new file mode 100644 index 00000000..7e0515ca --- /dev/null +++ b/jslib/common/src/models/request/emailTokenRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class EmailTokenRequest extends SecretVerificationRequest { + newEmail: string; + masterPasswordHash: string; +} diff --git a/jslib/common/src/models/request/emergencyAccessAcceptRequest.ts b/jslib/common/src/models/request/emergencyAccessAcceptRequest.ts new file mode 100644 index 00000000..1cb10253 --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessAcceptRequest.ts @@ -0,0 +1,3 @@ +export class EmergencyAccessAcceptRequest { + token: string; +} diff --git a/jslib/common/src/models/request/emergencyAccessConfirmRequest.ts b/jslib/common/src/models/request/emergencyAccessConfirmRequest.ts new file mode 100644 index 00000000..ee54a4fe --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessConfirmRequest.ts @@ -0,0 +1,3 @@ +export class EmergencyAccessConfirmRequest { + key: string; +} diff --git a/jslib/common/src/models/request/emergencyAccessInviteRequest.ts b/jslib/common/src/models/request/emergencyAccessInviteRequest.ts new file mode 100644 index 00000000..d75ed419 --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessInviteRequest.ts @@ -0,0 +1,7 @@ +import { EmergencyAccessType } from "../../enums/emergencyAccessType"; + +export class EmergencyAccessInviteRequest { + email: string; + type: EmergencyAccessType; + waitTimeDays: number; +} diff --git a/jslib/common/src/models/request/emergencyAccessPasswordRequest.ts b/jslib/common/src/models/request/emergencyAccessPasswordRequest.ts new file mode 100644 index 00000000..3fb459e1 --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessPasswordRequest.ts @@ -0,0 +1,4 @@ +export class EmergencyAccessPasswordRequest { + newMasterPasswordHash: string; + key: string; +} diff --git a/jslib/common/src/models/request/emergencyAccessUpdateRequest.ts b/jslib/common/src/models/request/emergencyAccessUpdateRequest.ts new file mode 100644 index 00000000..d7c55c94 --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessUpdateRequest.ts @@ -0,0 +1,7 @@ +import { EmergencyAccessType } from "../../enums/emergencyAccessType"; + +export class EmergencyAccessUpdateRequest { + type: EmergencyAccessType; + waitTimeDays: number; + keyEncrypted?: string; +} diff --git a/jslib/common/src/models/request/eventRequest.ts b/jslib/common/src/models/request/eventRequest.ts new file mode 100644 index 00000000..a6228fd6 --- /dev/null +++ b/jslib/common/src/models/request/eventRequest.ts @@ -0,0 +1,7 @@ +import { EventType } from "../../enums/eventType"; + +export class EventRequest { + type: EventType; + cipherId: string; + date: string; +} diff --git a/jslib/common/src/models/request/folderRequest.ts b/jslib/common/src/models/request/folderRequest.ts new file mode 100644 index 00000000..a37f66dd --- /dev/null +++ b/jslib/common/src/models/request/folderRequest.ts @@ -0,0 +1,9 @@ +import { Folder } from "../domain/folder"; + +export class FolderRequest { + name: string; + + constructor(folder: Folder) { + this.name = folder.name ? folder.name.encryptedString : null; + } +} diff --git a/jslib/common/src/models/request/folderWithIdRequest.ts b/jslib/common/src/models/request/folderWithIdRequest.ts new file mode 100644 index 00000000..a36c2b70 --- /dev/null +++ b/jslib/common/src/models/request/folderWithIdRequest.ts @@ -0,0 +1,12 @@ +import { Folder } from "../domain/folder"; + +import { FolderRequest } from "./folderRequest"; + +export class FolderWithIdRequest extends FolderRequest { + id: string; + + constructor(folder: Folder) { + super(folder); + this.id = folder.id; + } +} diff --git a/jslib/common/src/models/request/groupRequest.ts b/jslib/common/src/models/request/groupRequest.ts new file mode 100644 index 00000000..c4c349c8 --- /dev/null +++ b/jslib/common/src/models/request/groupRequest.ts @@ -0,0 +1,8 @@ +import { SelectionReadOnlyRequest } from "./selectionReadOnlyRequest"; + +export class GroupRequest { + name: string; + accessAll: boolean; + externalId: string; + collections: SelectionReadOnlyRequest[] = []; +} diff --git a/jslib/common/src/models/request/iapCheckRequest.ts b/jslib/common/src/models/request/iapCheckRequest.ts new file mode 100644 index 00000000..b8796e8c --- /dev/null +++ b/jslib/common/src/models/request/iapCheckRequest.ts @@ -0,0 +1,5 @@ +import { PaymentMethodType } from "../../enums/paymentMethodType"; + +export class IapCheckRequest { + paymentMethodType: PaymentMethodType; +} diff --git a/jslib/common/src/models/request/identityToken/apiTokenRequest.ts b/jslib/common/src/models/request/identityToken/apiTokenRequest.ts new file mode 100644 index 00000000..247ba174 --- /dev/null +++ b/jslib/common/src/models/request/identityToken/apiTokenRequest.ts @@ -0,0 +1,25 @@ +import { DeviceRequest } from "../deviceRequest"; + +import { TokenRequest } from "./tokenRequest"; +import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; + +export class ApiTokenRequest extends TokenRequest { + constructor( + public clientId: string, + public clientSecret: string, + protected twoFactor: TokenRequestTwoFactor, + device?: DeviceRequest + ) { + super(twoFactor, device); + } + + toIdentityToken() { + const obj = super.toIdentityToken(this.clientId); + + obj.scope = this.clientId.startsWith("organization") ? "api.organization" : "api"; + obj.grant_type = "client_credentials"; + obj.client_secret = this.clientSecret; + + return obj; + } +} diff --git a/jslib/common/src/models/request/identityToken/passwordTokenRequest.ts b/jslib/common/src/models/request/identityToken/passwordTokenRequest.ts new file mode 100644 index 00000000..eda10037 --- /dev/null +++ b/jslib/common/src/models/request/identityToken/passwordTokenRequest.ts @@ -0,0 +1,37 @@ +import { ClientType } from "../../../enums/clientType"; +import { Utils } from "../../../misc/utils"; +import { CaptchaProtectedRequest } from "../captchaProtectedRequest"; +import { DeviceRequest } from "../deviceRequest"; + +import { TokenRequest } from "./tokenRequest"; +import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; + +export class PasswordTokenRequest extends TokenRequest implements CaptchaProtectedRequest { + constructor( + public email: string, + public masterPasswordHash: string, + public captchaResponse: string, + protected twoFactor: TokenRequestTwoFactor, + device?: DeviceRequest + ) { + super(twoFactor, device); + } + + toIdentityToken(clientId: ClientType) { + const obj = super.toIdentityToken(clientId); + + obj.grant_type = "password"; + obj.username = this.email; + obj.password = this.masterPasswordHash; + + if (this.captchaResponse != null) { + obj.captchaResponse = this.captchaResponse; + } + + return obj; + } + + alterIdentityTokenHeaders(headers: Headers) { + headers.set("Auth-Email", Utils.fromUtf8ToUrlB64(this.email)); + } +} diff --git a/jslib/common/src/models/request/identityToken/ssoTokenRequest.ts b/jslib/common/src/models/request/identityToken/ssoTokenRequest.ts new file mode 100644 index 00000000..c7697c2f --- /dev/null +++ b/jslib/common/src/models/request/identityToken/ssoTokenRequest.ts @@ -0,0 +1,27 @@ +import { DeviceRequest } from "../deviceRequest"; + +import { TokenRequest } from "./tokenRequest"; +import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; + +export class SsoTokenRequest extends TokenRequest { + constructor( + public code: string, + public codeVerifier: string, + public redirectUri: string, + protected twoFactor: TokenRequestTwoFactor, + device?: DeviceRequest + ) { + super(twoFactor, device); + } + + toIdentityToken(clientId: string) { + const obj = super.toIdentityToken(clientId); + + obj.grant_type = "authorization_code"; + obj.code = this.code; + obj.code_verifier = this.codeVerifier; + obj.redirect_uri = this.redirectUri; + + return obj; + } +} diff --git a/jslib/common/src/models/request/identityToken/tokenRequest.ts b/jslib/common/src/models/request/identityToken/tokenRequest.ts new file mode 100644 index 00000000..82a4a394 --- /dev/null +++ b/jslib/common/src/models/request/identityToken/tokenRequest.ts @@ -0,0 +1,43 @@ +import { DeviceRequest } from "../deviceRequest"; + +import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; + +export abstract class TokenRequest { + protected device?: DeviceRequest; + + constructor(protected twoFactor: TokenRequestTwoFactor, device?: DeviceRequest) { + this.device = device != null ? device : null; + } + + // eslint-disable-next-line + alterIdentityTokenHeaders(headers: Headers) { + // Implemented in subclass if required + } + + setTwoFactor(twoFactor: TokenRequestTwoFactor) { + this.twoFactor = twoFactor; + } + + protected toIdentityToken(clientId: string) { + const obj: any = { + scope: "api offline_access", + client_id: clientId, + }; + + if (this.device) { + obj.deviceType = this.device.type; + obj.deviceIdentifier = this.device.identifier; + obj.deviceName = this.device.name; + // no push tokens for browser apps yet + // obj.devicePushToken = this.device.pushToken; + } + + if (this.twoFactor.token && this.twoFactor.provider != null) { + obj.twoFactorToken = this.twoFactor.token; + obj.twoFactorProvider = this.twoFactor.provider; + obj.twoFactorRemember = this.twoFactor.remember ? "1" : "0"; + } + + return obj; + } +} diff --git a/jslib/common/src/models/request/identityToken/tokenRequestTwoFactor.ts b/jslib/common/src/models/request/identityToken/tokenRequestTwoFactor.ts new file mode 100644 index 00000000..567e8c66 --- /dev/null +++ b/jslib/common/src/models/request/identityToken/tokenRequestTwoFactor.ts @@ -0,0 +1,9 @@ +import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType"; + +export class TokenRequestTwoFactor { + constructor( + public provider: TwoFactorProviderType = null, + public token: string = null, + public remember: boolean = false + ) {} +} diff --git a/jslib/common/src/models/request/importCiphersRequest.ts b/jslib/common/src/models/request/importCiphersRequest.ts new file mode 100644 index 00000000..ebaa03a3 --- /dev/null +++ b/jslib/common/src/models/request/importCiphersRequest.ts @@ -0,0 +1,9 @@ +import { CipherRequest } from "./cipherRequest"; +import { FolderRequest } from "./folderRequest"; +import { KvpRequest } from "./kvpRequest"; + +export class ImportCiphersRequest { + ciphers: CipherRequest[] = []; + folders: FolderRequest[] = []; + folderRelationships: KvpRequest[] = []; +} diff --git a/jslib/common/src/models/request/importDirectoryRequest.ts b/jslib/common/src/models/request/importDirectoryRequest.ts new file mode 100644 index 00000000..a13ce456 --- /dev/null +++ b/jslib/common/src/models/request/importDirectoryRequest.ts @@ -0,0 +1,9 @@ +import { ImportDirectoryRequestGroup } from "./importDirectoryRequestGroup"; +import { ImportDirectoryRequestUser } from "./importDirectoryRequestUser"; + +export class ImportDirectoryRequest { + groups: ImportDirectoryRequestGroup[] = []; + users: ImportDirectoryRequestUser[] = []; + overwriteExisting = false; + largeImport = false; +} diff --git a/jslib/common/src/models/request/importDirectoryRequestGroup.ts b/jslib/common/src/models/request/importDirectoryRequestGroup.ts new file mode 100644 index 00000000..4b7b3567 --- /dev/null +++ b/jslib/common/src/models/request/importDirectoryRequestGroup.ts @@ -0,0 +1,5 @@ +export class ImportDirectoryRequestGroup { + name: string; + externalId: string; + users: string[]; +} diff --git a/jslib/common/src/models/request/importDirectoryRequestUser.ts b/jslib/common/src/models/request/importDirectoryRequestUser.ts new file mode 100644 index 00000000..9dbf6a34 --- /dev/null +++ b/jslib/common/src/models/request/importDirectoryRequestUser.ts @@ -0,0 +1,5 @@ +export class ImportDirectoryRequestUser { + externalId: string; + email: string; + deleted: boolean; +} diff --git a/jslib/common/src/models/request/importOrganizationCiphersRequest.ts b/jslib/common/src/models/request/importOrganizationCiphersRequest.ts new file mode 100644 index 00000000..f2936afb --- /dev/null +++ b/jslib/common/src/models/request/importOrganizationCiphersRequest.ts @@ -0,0 +1,9 @@ +import { CipherRequest } from "./cipherRequest"; +import { CollectionRequest } from "./collectionRequest"; +import { KvpRequest } from "./kvpRequest"; + +export class ImportOrganizationCiphersRequest { + ciphers: CipherRequest[] = []; + collections: CollectionRequest[] = []; + collectionRelationships: KvpRequest[] = []; +} diff --git a/jslib/common/src/models/request/kdfRequest.ts b/jslib/common/src/models/request/kdfRequest.ts new file mode 100644 index 00000000..47c1ce07 --- /dev/null +++ b/jslib/common/src/models/request/kdfRequest.ts @@ -0,0 +1,8 @@ +import { KdfType } from "../../enums/kdfType"; + +import { PasswordRequest } from "./passwordRequest"; + +export class KdfRequest extends PasswordRequest { + kdf: KdfType; + kdfIterations: number; +} diff --git a/jslib/common/src/models/request/keyConnectorUserKeyRequest.ts b/jslib/common/src/models/request/keyConnectorUserKeyRequest.ts new file mode 100644 index 00000000..3df2db82 --- /dev/null +++ b/jslib/common/src/models/request/keyConnectorUserKeyRequest.ts @@ -0,0 +1,7 @@ +export class KeyConnectorUserKeyRequest { + key: string; + + constructor(key: string) { + this.key = key; + } +} diff --git a/jslib/common/src/models/request/keysRequest.ts b/jslib/common/src/models/request/keysRequest.ts new file mode 100644 index 00000000..da4144e9 --- /dev/null +++ b/jslib/common/src/models/request/keysRequest.ts @@ -0,0 +1,9 @@ +export class KeysRequest { + publicKey: string; + encryptedPrivateKey: string; + + constructor(publicKey: string, encryptedPrivateKey: string) { + this.publicKey = publicKey; + this.encryptedPrivateKey = encryptedPrivateKey; + } +} diff --git a/jslib/common/src/models/request/kvpRequest.ts b/jslib/common/src/models/request/kvpRequest.ts new file mode 100644 index 00000000..ca37a85d --- /dev/null +++ b/jslib/common/src/models/request/kvpRequest.ts @@ -0,0 +1,9 @@ +export class KvpRequest { + key: TK; + value: TV; + + constructor(key: TK, value: TV) { + this.key = key; + this.value = value; + } +} diff --git a/jslib/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts b/jslib/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts new file mode 100644 index 00000000..7cc854b6 --- /dev/null +++ b/jslib/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts @@ -0,0 +1,7 @@ +import { PlanSponsorshipType } from "../../../enums/planSponsorshipType"; + +export class OrganizationSponsorshipCreateRequest { + sponsoredEmail: string; + planSponsorshipType: PlanSponsorshipType; + friendlyName: string; +} diff --git a/jslib/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts b/jslib/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts new file mode 100644 index 00000000..4c73836e --- /dev/null +++ b/jslib/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts @@ -0,0 +1,6 @@ +import { PlanSponsorshipType } from "../../../enums/planSponsorshipType"; + +export class OrganizationSponsorshipRedeemRequest { + planSponsorshipType: PlanSponsorshipType; + sponsoredOrganizationId: string; +} diff --git a/jslib/common/src/models/request/organization/organizationSsoRequest.ts b/jslib/common/src/models/request/organization/organizationSsoRequest.ts new file mode 100644 index 00000000..e7fdb61c --- /dev/null +++ b/jslib/common/src/models/request/organization/organizationSsoRequest.ts @@ -0,0 +1,6 @@ +import { SsoConfigApi } from "../../api/ssoConfigApi"; + +export class OrganizationSsoRequest { + enabled = false; + data: SsoConfigApi; +} diff --git a/jslib/common/src/models/request/organizationCreateRequest.ts b/jslib/common/src/models/request/organizationCreateRequest.ts new file mode 100644 index 00000000..db713d90 --- /dev/null +++ b/jslib/common/src/models/request/organizationCreateRequest.ts @@ -0,0 +1,27 @@ +import { PaymentMethodType } from "../../enums/paymentMethodType"; +import { PlanType } from "../../enums/planType"; + +import { OrganizationKeysRequest } from "./organizationKeysRequest"; + +export class OrganizationCreateRequest { + name: string; + businessName: string; + billingEmail: string; + planType: PlanType; + key: string; + keys: OrganizationKeysRequest; + paymentMethodType: PaymentMethodType; + paymentToken: string; + additionalSeats: number; + maxAutoscaleSeats: number; + additionalStorageGb: number; + premiumAccessAddon: boolean; + collectionName: string; + taxIdNumber: string; + billingAddressLine1: string; + billingAddressLine2: string; + billingAddressCity: string; + billingAddressState: string; + billingAddressPostalCode: string; + billingAddressCountry: string; +} diff --git a/jslib/common/src/models/request/organizationImportGroupRequest.ts b/jslib/common/src/models/request/organizationImportGroupRequest.ts new file mode 100644 index 00000000..086ce653 --- /dev/null +++ b/jslib/common/src/models/request/organizationImportGroupRequest.ts @@ -0,0 +1,18 @@ +import { ImportDirectoryRequestGroup } from "./importDirectoryRequestGroup"; + +export class OrganizationImportGroupRequest { + name: string; + externalId: string; + memberExternalIds: string[]; + + constructor(model: Required | ImportDirectoryRequestGroup) { + this.name = model.name; + this.externalId = model.externalId; + + if (model instanceof ImportDirectoryRequestGroup) { + this.memberExternalIds = model.users; + } else { + this.memberExternalIds = model.memberExternalIds; + } + } +} diff --git a/jslib/common/src/models/request/organizationImportMemberRequest.ts b/jslib/common/src/models/request/organizationImportMemberRequest.ts new file mode 100644 index 00000000..8161365c --- /dev/null +++ b/jslib/common/src/models/request/organizationImportMemberRequest.ts @@ -0,0 +1,13 @@ +import { ImportDirectoryRequestUser } from "./importDirectoryRequestUser"; + +export class OrganizationImportMemberRequest { + email: string; + externalId: string; + deleted: boolean; + + constructor(model: Required | ImportDirectoryRequestUser) { + this.email = model.email; + this.externalId = model.externalId; + this.deleted = model.deleted; + } +} diff --git a/jslib/common/src/models/request/organizationImportRequest.ts b/jslib/common/src/models/request/organizationImportRequest.ts new file mode 100644 index 00000000..af7db2be --- /dev/null +++ b/jslib/common/src/models/request/organizationImportRequest.ts @@ -0,0 +1,31 @@ +import { ImportDirectoryRequest } from "./importDirectoryRequest"; +import { OrganizationImportGroupRequest } from "./organizationImportGroupRequest"; +import { OrganizationImportMemberRequest } from "./organizationImportMemberRequest"; + +export class OrganizationImportRequest { + groups: OrganizationImportGroupRequest[] = []; + members: OrganizationImportMemberRequest[] = []; + overwriteExisting = false; + largeImport = false; + + constructor( + model: + | { + groups: Required[]; + users: Required[]; + overwriteExisting: boolean; + largeImport: boolean; + } + | ImportDirectoryRequest + ) { + if (model instanceof ImportDirectoryRequest) { + this.groups = model.groups.map((g) => new OrganizationImportGroupRequest(g)); + this.members = model.users.map((u) => new OrganizationImportMemberRequest(u)); + } else { + this.groups = model.groups.map((g) => new OrganizationImportGroupRequest(g)); + this.members = model.users.map((u) => new OrganizationImportMemberRequest(u)); + } + this.overwriteExisting = model.overwriteExisting; + this.largeImport = model.largeImport; + } +} diff --git a/jslib/common/src/models/request/organizationKeysRequest.ts b/jslib/common/src/models/request/organizationKeysRequest.ts new file mode 100644 index 00000000..c63e05ab --- /dev/null +++ b/jslib/common/src/models/request/organizationKeysRequest.ts @@ -0,0 +1,7 @@ +import { KeysRequest } from "./keysRequest"; + +export class OrganizationKeysRequest extends KeysRequest { + constructor(publicKey: string, encryptedPrivateKey: string) { + super(publicKey, encryptedPrivateKey); + } +} diff --git a/jslib/common/src/models/request/organizationSubscriptionUpdateRequest.ts b/jslib/common/src/models/request/organizationSubscriptionUpdateRequest.ts new file mode 100644 index 00000000..9db3d4be --- /dev/null +++ b/jslib/common/src/models/request/organizationSubscriptionUpdateRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationSubscriptionUpdateRequest { + constructor(public seatAdjustment: number, public maxAutoscaleSeats?: number) {} +} diff --git a/jslib/common/src/models/request/organizationTaxInfoUpdateRequest.ts b/jslib/common/src/models/request/organizationTaxInfoUpdateRequest.ts new file mode 100644 index 00000000..283151f4 --- /dev/null +++ b/jslib/common/src/models/request/organizationTaxInfoUpdateRequest.ts @@ -0,0 +1,9 @@ +import { TaxInfoUpdateRequest } from "./taxInfoUpdateRequest"; + +export class OrganizationTaxInfoUpdateRequest extends TaxInfoUpdateRequest { + taxId: string; + line1: string; + line2: string; + city: string; + state: string; +} diff --git a/jslib/common/src/models/request/organizationUpdateRequest.ts b/jslib/common/src/models/request/organizationUpdateRequest.ts new file mode 100644 index 00000000..faddf641 --- /dev/null +++ b/jslib/common/src/models/request/organizationUpdateRequest.ts @@ -0,0 +1,9 @@ +import { OrganizationKeysRequest } from "./organizationKeysRequest"; + +export class OrganizationUpdateRequest { + name: string; + identifier: string; + businessName: string; + billingEmail: string; + keys: OrganizationKeysRequest; +} diff --git a/jslib/common/src/models/request/organizationUpgradeRequest.ts b/jslib/common/src/models/request/organizationUpgradeRequest.ts new file mode 100644 index 00000000..b62976fa --- /dev/null +++ b/jslib/common/src/models/request/organizationUpgradeRequest.ts @@ -0,0 +1,14 @@ +import { PlanType } from "../../enums/planType"; + +import { OrganizationKeysRequest } from "./organizationKeysRequest"; + +export class OrganizationUpgradeRequest { + businessName: string; + planType: PlanType; + additionalSeats: number; + additionalStorageGb: number; + premiumAccessAddon: boolean; + billingAddressCountry: string; + billingAddressPostalCode: string; + keys: OrganizationKeysRequest; +} diff --git a/jslib/common/src/models/request/organizationUserAcceptRequest.ts b/jslib/common/src/models/request/organizationUserAcceptRequest.ts new file mode 100644 index 00000000..c4b2a4d3 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserAcceptRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationUserAcceptRequest { + token: string; +} diff --git a/jslib/common/src/models/request/organizationUserBulkConfirmRequest.ts b/jslib/common/src/models/request/organizationUserBulkConfirmRequest.ts new file mode 100644 index 00000000..35e05602 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserBulkConfirmRequest.ts @@ -0,0 +1,12 @@ +type OrganizationUserBulkRequestEntry = { + id: string; + key: string; +}; + +export class OrganizationUserBulkConfirmRequest { + keys: OrganizationUserBulkRequestEntry[]; + + constructor(keys: OrganizationUserBulkRequestEntry[]) { + this.keys = keys; + } +} diff --git a/jslib/common/src/models/request/organizationUserBulkRequest.ts b/jslib/common/src/models/request/organizationUserBulkRequest.ts new file mode 100644 index 00000000..c73800eb --- /dev/null +++ b/jslib/common/src/models/request/organizationUserBulkRequest.ts @@ -0,0 +1,7 @@ +export class OrganizationUserBulkRequest { + ids: string[]; + + constructor(ids: string[]) { + this.ids = ids == null ? [] : ids; + } +} diff --git a/jslib/common/src/models/request/organizationUserConfirmRequest.ts b/jslib/common/src/models/request/organizationUserConfirmRequest.ts new file mode 100644 index 00000000..abd48749 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserConfirmRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationUserConfirmRequest { + key: string; +} diff --git a/jslib/common/src/models/request/organizationUserInviteRequest.ts b/jslib/common/src/models/request/organizationUserInviteRequest.ts new file mode 100644 index 00000000..7d037004 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserInviteRequest.ts @@ -0,0 +1,12 @@ +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { SelectionReadOnlyRequest } from "./selectionReadOnlyRequest"; + +export class OrganizationUserInviteRequest { + emails: string[] = []; + type: OrganizationUserType; + accessAll: boolean; + collections: SelectionReadOnlyRequest[] = []; + permissions: PermissionsApi; +} diff --git a/jslib/common/src/models/request/organizationUserResetPasswordEnrollmentRequest.ts b/jslib/common/src/models/request/organizationUserResetPasswordEnrollmentRequest.ts new file mode 100644 index 00000000..8d88164d --- /dev/null +++ b/jslib/common/src/models/request/organizationUserResetPasswordEnrollmentRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationUserResetPasswordEnrollmentRequest { + resetPasswordKey: string; +} diff --git a/jslib/common/src/models/request/organizationUserResetPasswordRequest.ts b/jslib/common/src/models/request/organizationUserResetPasswordRequest.ts new file mode 100644 index 00000000..b0c4e483 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserResetPasswordRequest.ts @@ -0,0 +1,4 @@ +export class OrganizationUserResetPasswordRequest { + newMasterPasswordHash: string; + key: string; +} diff --git a/jslib/common/src/models/request/organizationUserUpdateGroupsRequest.ts b/jslib/common/src/models/request/organizationUserUpdateGroupsRequest.ts new file mode 100644 index 00000000..cd30d940 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserUpdateGroupsRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationUserUpdateGroupsRequest { + groupIds: string[] = []; +} diff --git a/jslib/common/src/models/request/organizationUserUpdateRequest.ts b/jslib/common/src/models/request/organizationUserUpdateRequest.ts new file mode 100644 index 00000000..ec9ae3bc --- /dev/null +++ b/jslib/common/src/models/request/organizationUserUpdateRequest.ts @@ -0,0 +1,11 @@ +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { SelectionReadOnlyRequest } from "./selectionReadOnlyRequest"; + +export class OrganizationUserUpdateRequest { + type: OrganizationUserType; + accessAll: boolean; + collections: SelectionReadOnlyRequest[] = []; + permissions: PermissionsApi; +} diff --git a/jslib/common/src/models/request/passwordHintRequest.ts b/jslib/common/src/models/request/passwordHintRequest.ts new file mode 100644 index 00000000..7182e05e --- /dev/null +++ b/jslib/common/src/models/request/passwordHintRequest.ts @@ -0,0 +1,7 @@ +export class PasswordHintRequest { + email: string; + + constructor(email: string) { + this.email = email; + } +} diff --git a/jslib/common/src/models/request/passwordHistoryRequest.ts b/jslib/common/src/models/request/passwordHistoryRequest.ts new file mode 100644 index 00000000..6cca2b86 --- /dev/null +++ b/jslib/common/src/models/request/passwordHistoryRequest.ts @@ -0,0 +1,4 @@ +export class PasswordHistoryRequest { + password: string; + lastUsedDate: Date; +} diff --git a/jslib/common/src/models/request/passwordRequest.ts b/jslib/common/src/models/request/passwordRequest.ts new file mode 100644 index 00000000..9f7df7df --- /dev/null +++ b/jslib/common/src/models/request/passwordRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class PasswordRequest extends SecretVerificationRequest { + newMasterPasswordHash: string; + key: string; +} diff --git a/jslib/common/src/models/request/paymentRequest.ts b/jslib/common/src/models/request/paymentRequest.ts new file mode 100644 index 00000000..8e7f70a1 --- /dev/null +++ b/jslib/common/src/models/request/paymentRequest.ts @@ -0,0 +1,7 @@ +import { PaymentMethodType } from "../../enums/paymentMethodType"; +import { OrganizationTaxInfoUpdateRequest } from "../request/organizationTaxInfoUpdateRequest"; + +export class PaymentRequest extends OrganizationTaxInfoUpdateRequest { + paymentMethodType: PaymentMethodType; + paymentToken: string; +} diff --git a/jslib/common/src/models/request/policyRequest.ts b/jslib/common/src/models/request/policyRequest.ts new file mode 100644 index 00000000..98b05912 --- /dev/null +++ b/jslib/common/src/models/request/policyRequest.ts @@ -0,0 +1,7 @@ +import { PolicyType } from "../../enums/policyType"; + +export class PolicyRequest { + type: PolicyType; + enabled: boolean; + data: any; +} diff --git a/jslib/common/src/models/request/preloginRequest.ts b/jslib/common/src/models/request/preloginRequest.ts new file mode 100644 index 00000000..689204b7 --- /dev/null +++ b/jslib/common/src/models/request/preloginRequest.ts @@ -0,0 +1,7 @@ +export class PreloginRequest { + email: string; + + constructor(email: string) { + this.email = email; + } +} diff --git a/jslib/common/src/models/request/provider/providerAddOrganizationRequest.ts b/jslib/common/src/models/request/provider/providerAddOrganizationRequest.ts new file mode 100644 index 00000000..380eea1d --- /dev/null +++ b/jslib/common/src/models/request/provider/providerAddOrganizationRequest.ts @@ -0,0 +1,4 @@ +export class ProviderAddOrganizationRequest { + organizationId: string; + key: string; +} diff --git a/jslib/common/src/models/request/provider/providerOrganizationCreateRequest.ts b/jslib/common/src/models/request/provider/providerOrganizationCreateRequest.ts new file mode 100644 index 00000000..8d02f530 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerOrganizationCreateRequest.ts @@ -0,0 +1,8 @@ +import { OrganizationCreateRequest } from "../organizationCreateRequest"; + +export class ProviderOrganizationCreateRequest { + constructor( + public clientOwnerEmail: string, + public organizationCreateRequest: OrganizationCreateRequest + ) {} +} diff --git a/jslib/common/src/models/request/provider/providerSetupRequest.ts b/jslib/common/src/models/request/provider/providerSetupRequest.ts new file mode 100644 index 00000000..61eb943f --- /dev/null +++ b/jslib/common/src/models/request/provider/providerSetupRequest.ts @@ -0,0 +1,7 @@ +export class ProviderSetupRequest { + name: string; + businessName: string; + billingEmail: string; + token: string; + key: string; +} diff --git a/jslib/common/src/models/request/provider/providerUpdateRequest.ts b/jslib/common/src/models/request/provider/providerUpdateRequest.ts new file mode 100644 index 00000000..dafa7418 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUpdateRequest.ts @@ -0,0 +1,5 @@ +export class ProviderUpdateRequest { + name: string; + businessName: string; + billingEmail: string; +} diff --git a/jslib/common/src/models/request/provider/providerUserAcceptRequest.ts b/jslib/common/src/models/request/provider/providerUserAcceptRequest.ts new file mode 100644 index 00000000..0435e1df --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserAcceptRequest.ts @@ -0,0 +1,3 @@ +export class ProviderUserAcceptRequest { + token: string; +} diff --git a/jslib/common/src/models/request/provider/providerUserBulkConfirmRequest.ts b/jslib/common/src/models/request/provider/providerUserBulkConfirmRequest.ts new file mode 100644 index 00000000..76628b09 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserBulkConfirmRequest.ts @@ -0,0 +1,12 @@ +type ProviderUserBulkRequestEntry = { + id: string; + key: string; +}; + +export class ProviderUserBulkConfirmRequest { + keys: ProviderUserBulkRequestEntry[]; + + constructor(keys: ProviderUserBulkRequestEntry[]) { + this.keys = keys; + } +} diff --git a/jslib/common/src/models/request/provider/providerUserBulkRequest.ts b/jslib/common/src/models/request/provider/providerUserBulkRequest.ts new file mode 100644 index 00000000..f45ed1bb --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserBulkRequest.ts @@ -0,0 +1,7 @@ +export class ProviderUserBulkRequest { + ids: string[]; + + constructor(ids: string[]) { + this.ids = ids == null ? [] : ids; + } +} diff --git a/jslib/common/src/models/request/provider/providerUserConfirmRequest.ts b/jslib/common/src/models/request/provider/providerUserConfirmRequest.ts new file mode 100644 index 00000000..1b7d4a06 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserConfirmRequest.ts @@ -0,0 +1,3 @@ +export class ProviderUserConfirmRequest { + key: string; +} diff --git a/jslib/common/src/models/request/provider/providerUserInviteRequest.ts b/jslib/common/src/models/request/provider/providerUserInviteRequest.ts new file mode 100644 index 00000000..65d8ba67 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserInviteRequest.ts @@ -0,0 +1,6 @@ +import { ProviderUserType } from "../../../enums/providerUserType"; + +export class ProviderUserInviteRequest { + emails: string[] = []; + type: ProviderUserType; +} diff --git a/jslib/common/src/models/request/provider/providerUserUpdateRequest.ts b/jslib/common/src/models/request/provider/providerUserUpdateRequest.ts new file mode 100644 index 00000000..25efdd89 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserUpdateRequest.ts @@ -0,0 +1,5 @@ +import { ProviderUserType } from "../../../enums/providerUserType"; + +export class ProviderUserUpdateRequest { + type: ProviderUserType; +} diff --git a/jslib/common/src/models/request/referenceEventRequest.ts b/jslib/common/src/models/request/referenceEventRequest.ts new file mode 100644 index 00000000..7a0b535a --- /dev/null +++ b/jslib/common/src/models/request/referenceEventRequest.ts @@ -0,0 +1,5 @@ +export class ReferenceEventRequest { + id: string; + layout: string; + flow: string; +} diff --git a/jslib/common/src/models/request/registerRequest.ts b/jslib/common/src/models/request/registerRequest.ts new file mode 100644 index 00000000..8650c5c5 --- /dev/null +++ b/jslib/common/src/models/request/registerRequest.ts @@ -0,0 +1,26 @@ +import { KdfType } from "../../enums/kdfType"; + +import { CaptchaProtectedRequest } from "./captchaProtectedRequest"; +import { KeysRequest } from "./keysRequest"; +import { ReferenceEventRequest } from "./referenceEventRequest"; + +export class RegisterRequest implements CaptchaProtectedRequest { + masterPasswordHint: string; + keys: KeysRequest; + token: string; + organizationUserId: string; + + constructor( + public email: string, + public name: string, + public masterPasswordHash: string, + masterPasswordHint: string, + public key: string, + public kdf: KdfType, + public kdfIterations: number, + public referenceData: ReferenceEventRequest, + public captchaResponse: string + ) { + this.masterPasswordHint = masterPasswordHint ? masterPasswordHint : null; + } +} diff --git a/jslib/common/src/models/request/seatRequest.ts b/jslib/common/src/models/request/seatRequest.ts new file mode 100644 index 00000000..d60e41fa --- /dev/null +++ b/jslib/common/src/models/request/seatRequest.ts @@ -0,0 +1,3 @@ +export class SeatRequest { + seatAdjustment: number; +} diff --git a/jslib/common/src/models/request/secretVerificationRequest.ts b/jslib/common/src/models/request/secretVerificationRequest.ts new file mode 100644 index 00000000..c0170a3e --- /dev/null +++ b/jslib/common/src/models/request/secretVerificationRequest.ts @@ -0,0 +1,4 @@ +export class SecretVerificationRequest { + masterPasswordHash: string; + otp: string; +} diff --git a/jslib/common/src/models/request/selectionReadOnlyRequest.ts b/jslib/common/src/models/request/selectionReadOnlyRequest.ts new file mode 100644 index 00000000..7b007324 --- /dev/null +++ b/jslib/common/src/models/request/selectionReadOnlyRequest.ts @@ -0,0 +1,11 @@ +export class SelectionReadOnlyRequest { + id: string; + readOnly: boolean; + hidePasswords: boolean; + + constructor(id: string, readOnly: boolean, hidePasswords: boolean) { + this.id = id; + this.readOnly = readOnly; + this.hidePasswords = hidePasswords; + } +} diff --git a/jslib/common/src/models/request/sendAccessRequest.ts b/jslib/common/src/models/request/sendAccessRequest.ts new file mode 100644 index 00000000..7607b03c --- /dev/null +++ b/jslib/common/src/models/request/sendAccessRequest.ts @@ -0,0 +1,3 @@ +export class SendAccessRequest { + password: string; +} diff --git a/jslib/common/src/models/request/sendRequest.ts b/jslib/common/src/models/request/sendRequest.ts new file mode 100644 index 00000000..1c77204c --- /dev/null +++ b/jslib/common/src/models/request/sendRequest.ts @@ -0,0 +1,48 @@ +import { SendType } from "../../enums/sendType"; +import { SendFileApi } from "../api/sendFileApi"; +import { SendTextApi } from "../api/sendTextApi"; +import { Send } from "../domain/send"; + +export class SendRequest { + type: SendType; + fileLength?: number; + name: string; + notes: string; + key: string; + maxAccessCount?: number; + expirationDate: string; + deletionDate: string; + text: SendTextApi; + file: SendFileApi; + password: string; + disabled: boolean; + hideEmail: boolean; + + constructor(send: Send, fileLength?: number) { + this.type = send.type; + this.fileLength = fileLength; + this.name = send.name ? send.name.encryptedString : null; + this.notes = send.notes ? send.notes.encryptedString : null; + this.maxAccessCount = send.maxAccessCount; + this.expirationDate = send.expirationDate != null ? send.expirationDate.toISOString() : null; + this.deletionDate = send.deletionDate != null ? send.deletionDate.toISOString() : null; + this.key = send.key != null ? send.key.encryptedString : null; + this.password = send.password; + this.disabled = send.disabled; + this.hideEmail = send.hideEmail; + + switch (this.type) { + case SendType.Text: + this.text = new SendTextApi(); + this.text.text = send.text.text != null ? send.text.text.encryptedString : null; + this.text.hidden = send.text.hidden; + break; + case SendType.File: + this.file = new SendFileApi(); + this.file.fileName = send.file.fileName != null ? send.file.fileName.encryptedString : null; + break; + default: + break; + } + } +} diff --git a/jslib/common/src/models/request/sendWithIdRequest.ts b/jslib/common/src/models/request/sendWithIdRequest.ts new file mode 100644 index 00000000..8591a57e --- /dev/null +++ b/jslib/common/src/models/request/sendWithIdRequest.ts @@ -0,0 +1,12 @@ +import { Send } from "../domain/send"; + +import { SendRequest } from "./sendRequest"; + +export class SendWithIdRequest extends SendRequest { + id: string; + + constructor(send: Send) { + super(send); + this.id = send.id; + } +} diff --git a/jslib/common/src/models/request/setPasswordRequest.ts b/jslib/common/src/models/request/setPasswordRequest.ts new file mode 100644 index 00000000..919a132f --- /dev/null +++ b/jslib/common/src/models/request/setPasswordRequest.ts @@ -0,0 +1,31 @@ +import { KdfType } from "../../enums/kdfType"; + +import { KeysRequest } from "./keysRequest"; + +export class SetPasswordRequest { + masterPasswordHash: string; + key: string; + masterPasswordHint: string; + keys: KeysRequest; + kdf: KdfType; + kdfIterations: number; + orgIdentifier: string; + + constructor( + masterPasswordHash: string, + key: string, + masterPasswordHint: string, + kdf: KdfType, + kdfIterations: number, + orgIdentifier: string, + keys: KeysRequest + ) { + this.masterPasswordHash = masterPasswordHash; + this.key = key; + this.masterPasswordHint = masterPasswordHint; + this.kdf = kdf; + this.kdfIterations = kdfIterations; + this.orgIdentifier = orgIdentifier; + this.keys = keys; + } +} diff --git a/jslib/common/src/models/request/storageRequest.ts b/jslib/common/src/models/request/storageRequest.ts new file mode 100644 index 00000000..4b3b614d --- /dev/null +++ b/jslib/common/src/models/request/storageRequest.ts @@ -0,0 +1,3 @@ +export class StorageRequest { + storageGbAdjustment: number; +} diff --git a/jslib/common/src/models/request/taxInfoUpdateRequest.ts b/jslib/common/src/models/request/taxInfoUpdateRequest.ts new file mode 100644 index 00000000..6881a8a4 --- /dev/null +++ b/jslib/common/src/models/request/taxInfoUpdateRequest.ts @@ -0,0 +1,4 @@ +export class TaxInfoUpdateRequest { + country: string; + postalCode: string; +} diff --git a/jslib/common/src/models/request/twoFactorEmailRequest.ts b/jslib/common/src/models/request/twoFactorEmailRequest.ts new file mode 100644 index 00000000..36c168b6 --- /dev/null +++ b/jslib/common/src/models/request/twoFactorEmailRequest.ts @@ -0,0 +1,5 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class TwoFactorEmailRequest extends SecretVerificationRequest { + email: string; +} diff --git a/jslib/common/src/models/request/twoFactorProviderRequest.ts b/jslib/common/src/models/request/twoFactorProviderRequest.ts new file mode 100644 index 00000000..9d606bc9 --- /dev/null +++ b/jslib/common/src/models/request/twoFactorProviderRequest.ts @@ -0,0 +1,7 @@ +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; + +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class TwoFactorProviderRequest extends SecretVerificationRequest { + type: TwoFactorProviderType; +} diff --git a/jslib/common/src/models/request/twoFactorRecoveryRequest.ts b/jslib/common/src/models/request/twoFactorRecoveryRequest.ts new file mode 100644 index 00000000..39135a37 --- /dev/null +++ b/jslib/common/src/models/request/twoFactorRecoveryRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class TwoFactorRecoveryRequest extends SecretVerificationRequest { + recoveryCode: string; + email: string; +} diff --git a/jslib/common/src/models/request/updateDomainsRequest.ts b/jslib/common/src/models/request/updateDomainsRequest.ts new file mode 100644 index 00000000..528d3682 --- /dev/null +++ b/jslib/common/src/models/request/updateDomainsRequest.ts @@ -0,0 +1,4 @@ +export class UpdateDomainsRequest { + equivalentDomains: string[][]; + excludedGlobalEquivalentDomains: number[]; +} diff --git a/jslib/common/src/models/request/updateKeyRequest.ts b/jslib/common/src/models/request/updateKeyRequest.ts new file mode 100644 index 00000000..a9fceffd --- /dev/null +++ b/jslib/common/src/models/request/updateKeyRequest.ts @@ -0,0 +1,12 @@ +import { CipherWithIdRequest } from "./cipherWithIdRequest"; +import { FolderWithIdRequest } from "./folderWithIdRequest"; +import { SendWithIdRequest } from "./sendWithIdRequest"; + +export class UpdateKeyRequest { + ciphers: CipherWithIdRequest[] = []; + folders: FolderWithIdRequest[] = []; + sends: SendWithIdRequest[] = []; + masterPasswordHash: string; + privateKey: string; + key: string; +} diff --git a/jslib/common/src/models/request/updateProfileRequest.ts b/jslib/common/src/models/request/updateProfileRequest.ts new file mode 100644 index 00000000..14ed554d --- /dev/null +++ b/jslib/common/src/models/request/updateProfileRequest.ts @@ -0,0 +1,10 @@ +export class UpdateProfileRequest { + name: string; + masterPasswordHint: string; + culture = "en-US"; // deprecated + + constructor(name: string, masterPasswordHint: string) { + this.name = name; + this.masterPasswordHint = masterPasswordHint ? masterPasswordHint : null; + } +} diff --git a/jslib/common/src/models/request/updateTempPasswordRequest.ts b/jslib/common/src/models/request/updateTempPasswordRequest.ts new file mode 100644 index 00000000..1bd97697 --- /dev/null +++ b/jslib/common/src/models/request/updateTempPasswordRequest.ts @@ -0,0 +1,5 @@ +import { OrganizationUserResetPasswordRequest } from "./organizationUserResetPasswordRequest"; + +export class UpdateTempPasswordRequest extends OrganizationUserResetPasswordRequest { + masterPasswordHint: string; +} diff --git a/jslib/common/src/models/request/updateTwoFactorAuthenticatorRequest.ts b/jslib/common/src/models/request/updateTwoFactorAuthenticatorRequest.ts new file mode 100644 index 00000000..9b718287 --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorAuthenticatorRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorAuthenticatorRequest extends SecretVerificationRequest { + token: string; + key: string; +} diff --git a/jslib/common/src/models/request/updateTwoFactorDuoRequest.ts b/jslib/common/src/models/request/updateTwoFactorDuoRequest.ts new file mode 100644 index 00000000..cd82215d --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorDuoRequest.ts @@ -0,0 +1,7 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorDuoRequest extends SecretVerificationRequest { + integrationKey: string; + secretKey: string; + host: string; +} diff --git a/jslib/common/src/models/request/updateTwoFactorEmailRequest.ts b/jslib/common/src/models/request/updateTwoFactorEmailRequest.ts new file mode 100644 index 00000000..be7da1fd --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorEmailRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorEmailRequest extends SecretVerificationRequest { + token: string; + email: string; +} diff --git a/jslib/common/src/models/request/updateTwoFactorWebAuthnDeleteRequest.ts b/jslib/common/src/models/request/updateTwoFactorWebAuthnDeleteRequest.ts new file mode 100644 index 00000000..1decda72 --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorWebAuthnDeleteRequest.ts @@ -0,0 +1,5 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorWebAuthnDeleteRequest extends SecretVerificationRequest { + id: number; +} diff --git a/jslib/common/src/models/request/updateTwoFactorWebAuthnRequest.ts b/jslib/common/src/models/request/updateTwoFactorWebAuthnRequest.ts new file mode 100644 index 00000000..2e788b52 --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorWebAuthnRequest.ts @@ -0,0 +1,7 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorWebAuthnRequest extends SecretVerificationRequest { + deviceResponse: PublicKeyCredential; + name: string; + id: number; +} diff --git a/jslib/common/src/models/request/updateTwoFactorYubioOtpRequest.ts b/jslib/common/src/models/request/updateTwoFactorYubioOtpRequest.ts new file mode 100644 index 00000000..cb495b1e --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorYubioOtpRequest.ts @@ -0,0 +1,10 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorYubioOtpRequest extends SecretVerificationRequest { + key1: string; + key2: string; + key3: string; + key4: string; + key5: string; + nfc: boolean; +} diff --git a/jslib/common/src/models/request/verifyBankRequest.ts b/jslib/common/src/models/request/verifyBankRequest.ts new file mode 100644 index 00000000..823eaf46 --- /dev/null +++ b/jslib/common/src/models/request/verifyBankRequest.ts @@ -0,0 +1,4 @@ +export class VerifyBankRequest { + amount1: number; + amount2: number; +} diff --git a/jslib/common/src/models/request/verifyDeleteRecoverRequest.ts b/jslib/common/src/models/request/verifyDeleteRecoverRequest.ts new file mode 100644 index 00000000..2374d32d --- /dev/null +++ b/jslib/common/src/models/request/verifyDeleteRecoverRequest.ts @@ -0,0 +1,9 @@ +export class VerifyDeleteRecoverRequest { + userId: string; + token: string; + + constructor(userId: string, token: string) { + this.userId = userId; + this.token = token; + } +} diff --git a/jslib/common/src/models/request/verifyEmailRequest.ts b/jslib/common/src/models/request/verifyEmailRequest.ts new file mode 100644 index 00000000..ecee0190 --- /dev/null +++ b/jslib/common/src/models/request/verifyEmailRequest.ts @@ -0,0 +1,9 @@ +export class VerifyEmailRequest { + userId: string; + token: string; + + constructor(userId: string, token: string) { + this.userId = userId; + this.token = token; + } +} diff --git a/jslib/common/src/models/response/apiKeyResponse.ts b/jslib/common/src/models/response/apiKeyResponse.ts new file mode 100644 index 00000000..f1a24be0 --- /dev/null +++ b/jslib/common/src/models/response/apiKeyResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from "./baseResponse"; + +export class ApiKeyResponse extends BaseResponse { + apiKey: string; + + constructor(response: any) { + super(response); + this.apiKey = this.getResponseProperty("ApiKey"); + } +} diff --git a/jslib/common/src/models/response/attachmentResponse.ts b/jslib/common/src/models/response/attachmentResponse.ts new file mode 100644 index 00000000..a9ebfaf3 --- /dev/null +++ b/jslib/common/src/models/response/attachmentResponse.ts @@ -0,0 +1,20 @@ +import { BaseResponse } from "./baseResponse"; + +export class AttachmentResponse extends BaseResponse { + id: string; + url: string; + fileName: string; + key: string; + size: string; + sizeName: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.url = this.getResponseProperty("Url"); + this.fileName = this.getResponseProperty("FileName"); + this.key = this.getResponseProperty("Key"); + this.size = this.getResponseProperty("Size"); + this.sizeName = this.getResponseProperty("SizeName"); + } +} diff --git a/jslib/common/src/models/response/attachmentUploadDataResponse.ts b/jslib/common/src/models/response/attachmentUploadDataResponse.ts new file mode 100644 index 00000000..4520c309 --- /dev/null +++ b/jslib/common/src/models/response/attachmentUploadDataResponse.ts @@ -0,0 +1,23 @@ +import { FileUploadType } from "../../enums/fileUploadType"; + +import { BaseResponse } from "./baseResponse"; +import { CipherResponse } from "./cipherResponse"; + +export class AttachmentUploadDataResponse extends BaseResponse { + attachmentId: string; + fileUploadType: FileUploadType; + cipherResponse: CipherResponse; + cipherMiniResponse: CipherResponse; + url: string = null; + constructor(response: any) { + super(response); + this.attachmentId = this.getResponseProperty("AttachmentId"); + this.fileUploadType = this.getResponseProperty("FileUploadType"); + const cipherResponse = this.getResponseProperty("CipherResponse"); + const cipherMiniResponse = this.getResponseProperty("CipherMiniResponse"); + this.cipherResponse = cipherResponse == null ? null : new CipherResponse(cipherResponse); + this.cipherMiniResponse = + cipherMiniResponse == null ? null : new CipherResponse(cipherMiniResponse); + this.url = this.getResponseProperty("Url"); + } +} diff --git a/jslib/common/src/models/response/baseResponse.ts b/jslib/common/src/models/response/baseResponse.ts new file mode 100644 index 00000000..8c599fba --- /dev/null +++ b/jslib/common/src/models/response/baseResponse.ts @@ -0,0 +1,43 @@ +export abstract class BaseResponse { + private response: any; + + constructor(response: any) { + this.response = response; + } + + protected getResponseProperty( + propertyName: string, + response: any = null, + exactName = false + ): any { + if (propertyName == null || propertyName === "") { + throw new Error("propertyName must not be null/empty."); + } + if (response == null && this.response != null) { + response = this.response; + } + if (response == null) { + return null; + } + if (!exactName && response[propertyName] === undefined) { + let otherCasePropertyName: string = null; + if (propertyName.charAt(0) === propertyName.charAt(0).toUpperCase()) { + otherCasePropertyName = propertyName.charAt(0).toLowerCase(); + } else { + otherCasePropertyName = propertyName.charAt(0).toUpperCase(); + } + if (propertyName.length > 1) { + otherCasePropertyName += propertyName.slice(1); + } + + propertyName = otherCasePropertyName; + if (response[propertyName] === undefined) { + propertyName = propertyName.toLowerCase(); + } + if (response[propertyName] === undefined) { + propertyName = propertyName.toUpperCase(); + } + } + return response[propertyName]; + } +} diff --git a/jslib/common/src/models/response/billingResponse.ts b/jslib/common/src/models/response/billingResponse.ts new file mode 100644 index 00000000..8d09cf93 --- /dev/null +++ b/jslib/common/src/models/response/billingResponse.ts @@ -0,0 +1,83 @@ +import { PaymentMethodType } from "../../enums/paymentMethodType"; +import { TransactionType } from "../../enums/transactionType"; + +import { BaseResponse } from "./baseResponse"; + +export class BillingResponse extends BaseResponse { + balance: number; + paymentSource: BillingSourceResponse; + invoices: BillingInvoiceResponse[] = []; + transactions: BillingTransactionResponse[] = []; + + constructor(response: any) { + super(response); + this.balance = this.getResponseProperty("Balance"); + const paymentSource = this.getResponseProperty("PaymentSource"); + const transactions = this.getResponseProperty("Transactions"); + const invoices = this.getResponseProperty("Invoices"); + this.paymentSource = paymentSource == null ? null : new BillingSourceResponse(paymentSource); + if (transactions != null) { + this.transactions = transactions.map((t: any) => new BillingTransactionResponse(t)); + } + if (invoices != null) { + this.invoices = invoices.map((i: any) => new BillingInvoiceResponse(i)); + } + } +} + +export class BillingSourceResponse extends BaseResponse { + type: PaymentMethodType; + cardBrand: string; + description: string; + needsVerification: boolean; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + this.cardBrand = this.getResponseProperty("CardBrand"); + this.description = this.getResponseProperty("Description"); + this.needsVerification = this.getResponseProperty("NeedsVerification"); + } +} + +export class BillingInvoiceResponse extends BaseResponse { + url: string; + pdfUrl: string; + number: string; + paid: boolean; + date: string; + amount: number; + + constructor(response: any) { + super(response); + this.url = this.getResponseProperty("Url"); + this.pdfUrl = this.getResponseProperty("PdfUrl"); + this.number = this.getResponseProperty("Number"); + this.paid = this.getResponseProperty("Paid"); + this.date = this.getResponseProperty("Date"); + this.amount = this.getResponseProperty("Amount"); + } +} + +export class BillingTransactionResponse extends BaseResponse { + createdDate: string; + amount: number; + refunded: boolean; + partiallyRefunded: boolean; + refundedAmount: number; + type: TransactionType; + paymentMethodType: PaymentMethodType; + details: string; + + constructor(response: any) { + super(response); + this.createdDate = this.getResponseProperty("CreatedDate"); + this.amount = this.getResponseProperty("Amount"); + this.refunded = this.getResponseProperty("Refunded"); + this.partiallyRefunded = this.getResponseProperty("PartiallyRefunded"); + this.refundedAmount = this.getResponseProperty("RefundedAmount"); + this.type = this.getResponseProperty("Type"); + this.paymentMethodType = this.getResponseProperty("PaymentMethodType"); + this.details = this.getResponseProperty("Details"); + } +} diff --git a/jslib/common/src/models/response/breachAccountResponse.ts b/jslib/common/src/models/response/breachAccountResponse.ts new file mode 100644 index 00000000..00d39acc --- /dev/null +++ b/jslib/common/src/models/response/breachAccountResponse.ts @@ -0,0 +1,32 @@ +import { BaseResponse } from "./baseResponse"; + +export class BreachAccountResponse extends BaseResponse { + addedDate: string; + breachDate: string; + dataClasses: string[]; + description: string; + domain: string; + isActive: boolean; + isVerified: boolean; + logoPath: string; + modifiedDate: string; + name: string; + pwnCount: number; + title: string; + + constructor(response: any) { + super(response); + this.addedDate = this.getResponseProperty("AddedDate"); + this.breachDate = this.getResponseProperty("BreachDate"); + this.dataClasses = this.getResponseProperty("DataClasses"); + this.description = this.getResponseProperty("Description"); + this.domain = this.getResponseProperty("Domain"); + this.isActive = this.getResponseProperty("IsActive"); + this.isVerified = this.getResponseProperty("IsVerified"); + this.logoPath = this.getResponseProperty("LogoPath"); + this.modifiedDate = this.getResponseProperty("ModifiedDate"); + this.name = this.getResponseProperty("Name"); + this.pwnCount = this.getResponseProperty("PwnCount"); + this.title = this.getResponseProperty("Title"); + } +} diff --git a/jslib/common/src/models/response/cipherResponse.ts b/jslib/common/src/models/response/cipherResponse.ts new file mode 100644 index 00000000..5b461438 --- /dev/null +++ b/jslib/common/src/models/response/cipherResponse.ts @@ -0,0 +1,92 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CardApi } from "../api/cardApi"; +import { FieldApi } from "../api/fieldApi"; +import { IdentityApi } from "../api/identityApi"; +import { LoginApi } from "../api/loginApi"; +import { SecureNoteApi } from "../api/secureNoteApi"; + +import { AttachmentResponse } from "./attachmentResponse"; +import { BaseResponse } from "./baseResponse"; +import { PasswordHistoryResponse } from "./passwordHistoryResponse"; + +export class CipherResponse extends BaseResponse { + id: string; + organizationId: string; + folderId: string; + type: number; + name: string; + notes: string; + fields: FieldApi[]; + login: LoginApi; + card: CardApi; + identity: IdentityApi; + secureNote: SecureNoteApi; + favorite: boolean; + edit: boolean; + viewPassword: boolean; + organizationUseTotp: boolean; + revisionDate: string; + attachments: AttachmentResponse[]; + passwordHistory: PasswordHistoryResponse[]; + collectionIds: string[]; + deletedDate: string; + reprompt: CipherRepromptType; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.folderId = this.getResponseProperty("FolderId") || null; + this.type = this.getResponseProperty("Type"); + this.name = this.getResponseProperty("Name"); + this.notes = this.getResponseProperty("Notes"); + this.favorite = this.getResponseProperty("Favorite") || false; + this.edit = !!this.getResponseProperty("Edit"); + if (this.getResponseProperty("ViewPassword") == null) { + this.viewPassword = true; + } else { + this.viewPassword = this.getResponseProperty("ViewPassword"); + } + this.organizationUseTotp = this.getResponseProperty("OrganizationUseTotp"); + this.revisionDate = this.getResponseProperty("RevisionDate"); + this.collectionIds = this.getResponseProperty("CollectionIds"); + this.deletedDate = this.getResponseProperty("DeletedDate"); + + const login = this.getResponseProperty("Login"); + if (login != null) { + this.login = new LoginApi(login); + } + + const card = this.getResponseProperty("Card"); + if (card != null) { + this.card = new CardApi(card); + } + + const identity = this.getResponseProperty("Identity"); + if (identity != null) { + this.identity = new IdentityApi(identity); + } + + const secureNote = this.getResponseProperty("SecureNote"); + if (secureNote != null) { + this.secureNote = new SecureNoteApi(secureNote); + } + + const fields = this.getResponseProperty("Fields"); + if (fields != null) { + this.fields = fields.map((f: any) => new FieldApi(f)); + } + + const attachments = this.getResponseProperty("Attachments"); + if (attachments != null) { + this.attachments = attachments.map((a: any) => new AttachmentResponse(a)); + } + + const passwordHistory = this.getResponseProperty("PasswordHistory"); + if (passwordHistory != null) { + this.passwordHistory = passwordHistory.map((h: any) => new PasswordHistoryResponse(h)); + } + + this.reprompt = this.getResponseProperty("Reprompt") || CipherRepromptType.None; + } +} diff --git a/jslib/common/src/models/response/collectionResponse.ts b/jslib/common/src/models/response/collectionResponse.ts new file mode 100644 index 00000000..2f677f7d --- /dev/null +++ b/jslib/common/src/models/response/collectionResponse.ts @@ -0,0 +1,38 @@ +import { BaseResponse } from "./baseResponse"; +import { SelectionReadOnlyResponse } from "./selectionReadOnlyResponse"; + +export class CollectionResponse extends BaseResponse { + id: string; + organizationId: string; + name: string; + externalId: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.name = this.getResponseProperty("Name"); + this.externalId = this.getResponseProperty("ExternalId"); + } +} + +export class CollectionDetailsResponse extends CollectionResponse { + readOnly: boolean; + + constructor(response: any) { + super(response); + this.readOnly = this.getResponseProperty("ReadOnly") || false; + } +} + +export class CollectionGroupDetailsResponse extends CollectionResponse { + groups: SelectionReadOnlyResponse[] = []; + + constructor(response: any) { + super(response); + const groups = this.getResponseProperty("Groups"); + if (groups != null) { + this.groups = groups.map((g: any) => new SelectionReadOnlyResponse(g)); + } + } +} diff --git a/jslib/common/src/models/response/deviceResponse.ts b/jslib/common/src/models/response/deviceResponse.ts new file mode 100644 index 00000000..aac9ac6c --- /dev/null +++ b/jslib/common/src/models/response/deviceResponse.ts @@ -0,0 +1,20 @@ +import { DeviceType } from "../../enums/deviceType"; + +import { BaseResponse } from "./baseResponse"; + +export class DeviceResponse extends BaseResponse { + id: string; + name: number; + identifier: string; + type: DeviceType; + creationDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.identifier = this.getResponseProperty("Identifier"); + this.type = this.getResponseProperty("Type"); + this.creationDate = this.getResponseProperty("CreationDate"); + } +} diff --git a/jslib/common/src/models/response/domainsResponse.ts b/jslib/common/src/models/response/domainsResponse.ts new file mode 100644 index 00000000..3014c2f2 --- /dev/null +++ b/jslib/common/src/models/response/domainsResponse.ts @@ -0,0 +1,20 @@ +import { BaseResponse } from "./baseResponse"; +import { GlobalDomainResponse } from "./globalDomainResponse"; + +export class DomainsResponse extends BaseResponse { + equivalentDomains: string[][]; + globalEquivalentDomains: GlobalDomainResponse[] = []; + + constructor(response: any) { + super(response); + this.equivalentDomains = this.getResponseProperty("EquivalentDomains"); + const globalEquivalentDomains = this.getResponseProperty("GlobalEquivalentDomains"); + if (globalEquivalentDomains != null) { + this.globalEquivalentDomains = globalEquivalentDomains.map( + (d: any) => new GlobalDomainResponse(d) + ); + } else { + this.globalEquivalentDomains = []; + } + } +} diff --git a/jslib/common/src/models/response/emergencyAccessResponse.ts b/jslib/common/src/models/response/emergencyAccessResponse.ts new file mode 100644 index 00000000..9ac5380d --- /dev/null +++ b/jslib/common/src/models/response/emergencyAccessResponse.ts @@ -0,0 +1,82 @@ +import { EmergencyAccessStatusType } from "../../enums/emergencyAccessStatusType"; +import { EmergencyAccessType } from "../../enums/emergencyAccessType"; +import { KdfType } from "../../enums/kdfType"; + +import { BaseResponse } from "./baseResponse"; +import { CipherResponse } from "./cipherResponse"; + +export class EmergencyAccessGranteeDetailsResponse extends BaseResponse { + id: string; + granteeId: string; + name: string; + email: string; + type: EmergencyAccessType; + status: EmergencyAccessStatusType; + waitTimeDays: number; + creationDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.granteeId = this.getResponseProperty("GranteeId"); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + this.type = this.getResponseProperty("Type"); + this.status = this.getResponseProperty("Status"); + this.waitTimeDays = this.getResponseProperty("WaitTimeDays"); + this.creationDate = this.getResponseProperty("CreationDate"); + } +} + +export class EmergencyAccessGrantorDetailsResponse extends BaseResponse { + id: string; + grantorId: string; + name: string; + email: string; + type: EmergencyAccessType; + status: EmergencyAccessStatusType; + waitTimeDays: number; + creationDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.grantorId = this.getResponseProperty("GrantorId"); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + this.type = this.getResponseProperty("Type"); + this.status = this.getResponseProperty("Status"); + this.waitTimeDays = this.getResponseProperty("WaitTimeDays"); + this.creationDate = this.getResponseProperty("CreationDate"); + } +} + +export class EmergencyAccessTakeoverResponse extends BaseResponse { + keyEncrypted: string; + kdf: KdfType; + kdfIterations: number; + + constructor(response: any) { + super(response); + + this.keyEncrypted = this.getResponseProperty("KeyEncrypted"); + this.kdf = this.getResponseProperty("Kdf"); + this.kdfIterations = this.getResponseProperty("KdfIterations"); + } +} + +export class EmergencyAccessViewResponse extends BaseResponse { + keyEncrypted: string; + ciphers: CipherResponse[] = []; + + constructor(response: any) { + super(response); + + this.keyEncrypted = this.getResponseProperty("KeyEncrypted"); + + const ciphers = this.getResponseProperty("Ciphers"); + if (ciphers != null) { + this.ciphers = ciphers.map((c: any) => new CipherResponse(c)); + } + } +} diff --git a/jslib/common/src/models/response/errorResponse.ts b/jslib/common/src/models/response/errorResponse.ts new file mode 100644 index 00000000..3a0d22e6 --- /dev/null +++ b/jslib/common/src/models/response/errorResponse.ts @@ -0,0 +1,74 @@ +import { Utils } from "../../misc/utils"; + +import { BaseResponse } from "./baseResponse"; + +export class ErrorResponse extends BaseResponse { + message: string; + validationErrors: { [key: string]: string[] }; + statusCode: number; + captchaRequired: boolean; + captchaSiteKey: string; + + constructor(response: any, status: number, identityResponse?: boolean) { + super(response); + let errorModel = null; + if (response != null) { + const responseErrorModel = this.getResponseProperty("ErrorModel"); + if (responseErrorModel && identityResponse) { + errorModel = responseErrorModel; + } else { + errorModel = response; + } + } + + if (errorModel) { + this.message = this.getResponseProperty("Message", errorModel); + this.validationErrors = this.getResponseProperty("ValidationErrors", errorModel); + this.captchaSiteKey = this.validationErrors?.HCaptcha_SiteKey?.[0]; + this.captchaRequired = !Utils.isNullOrWhitespace(this.captchaSiteKey); + } else { + if (status === 429) { + this.message = "Rate limit exceeded. Try again later."; + } + } + this.statusCode = status; + } + + getSingleMessage(): string { + if (this.validationErrors == null) { + return this.message; + } + for (const key in this.validationErrors) { + // eslint-disable-next-line + if (!this.validationErrors.hasOwnProperty(key)) { + continue; + } + if (this.validationErrors[key].length) { + return this.validationErrors[key][0]; + } + } + return this.message; + } + + getAllMessages(): string[] { + const messages: string[] = []; + if (this.validationErrors == null) { + return messages; + } + for (const key in this.validationErrors) { + // eslint-disable-next-line + if (!this.validationErrors.hasOwnProperty(key)) { + continue; + } + this.validationErrors[key].forEach((item: string) => { + let prefix = ""; + if (key.indexOf("[") > -1 && key.indexOf("]") > -1) { + const lastSep = key.lastIndexOf("."); + prefix = key.substr(0, lastSep > -1 ? lastSep : key.length) + ": "; + } + messages.push(prefix + item); + }); + } + return messages; + } +} diff --git a/jslib/common/src/models/response/eventResponse.ts b/jslib/common/src/models/response/eventResponse.ts new file mode 100644 index 00000000..93e31955 --- /dev/null +++ b/jslib/common/src/models/response/eventResponse.ts @@ -0,0 +1,41 @@ +import { DeviceType } from "../../enums/deviceType"; +import { EventType } from "../../enums/eventType"; + +import { BaseResponse } from "./baseResponse"; + +export class EventResponse extends BaseResponse { + type: EventType; + userId: string; + organizationId: string; + providerId: string; + cipherId: string; + collectionId: string; + groupId: string; + policyId: string; + organizationUserId: string; + providerUserId: string; + providerOrganizationId: string; + actingUserId: string; + date: string; + deviceType: DeviceType; + ipAddress: string; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + this.userId = this.getResponseProperty("UserId"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.providerId = this.getResponseProperty("ProviderId"); + this.cipherId = this.getResponseProperty("CipherId"); + this.collectionId = this.getResponseProperty("CollectionId"); + this.groupId = this.getResponseProperty("GroupId"); + this.policyId = this.getResponseProperty("PolicyId"); + this.organizationUserId = this.getResponseProperty("OrganizationUserId"); + this.providerUserId = this.getResponseProperty("ProviderUserId"); + this.providerOrganizationId = this.getResponseProperty("ProviderOrganizationId"); + this.actingUserId = this.getResponseProperty("ActingUserId"); + this.date = this.getResponseProperty("Date"); + this.deviceType = this.getResponseProperty("DeviceType"); + this.ipAddress = this.getResponseProperty("IpAddress"); + } +} diff --git a/jslib/common/src/models/response/folderResponse.ts b/jslib/common/src/models/response/folderResponse.ts new file mode 100644 index 00000000..70d4897f --- /dev/null +++ b/jslib/common/src/models/response/folderResponse.ts @@ -0,0 +1,14 @@ +import { BaseResponse } from "./baseResponse"; + +export class FolderResponse extends BaseResponse { + id: string; + name: string; + revisionDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.revisionDate = this.getResponseProperty("RevisionDate"); + } +} diff --git a/jslib/common/src/models/response/globalDomainResponse.ts b/jslib/common/src/models/response/globalDomainResponse.ts new file mode 100644 index 00000000..e3197e76 --- /dev/null +++ b/jslib/common/src/models/response/globalDomainResponse.ts @@ -0,0 +1,14 @@ +import { BaseResponse } from "./baseResponse"; + +export class GlobalDomainResponse extends BaseResponse { + type: number; + domains: string[]; + excluded: boolean; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + this.domains = this.getResponseProperty("Domains"); + this.excluded = this.getResponseProperty("Excluded"); + } +} diff --git a/jslib/common/src/models/response/groupResponse.ts b/jslib/common/src/models/response/groupResponse.ts new file mode 100644 index 00000000..ab4b58b7 --- /dev/null +++ b/jslib/common/src/models/response/groupResponse.ts @@ -0,0 +1,31 @@ +import { BaseResponse } from "./baseResponse"; +import { SelectionReadOnlyResponse } from "./selectionReadOnlyResponse"; + +export class GroupResponse extends BaseResponse { + id: string; + organizationId: string; + name: string; + accessAll: boolean; + externalId: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.name = this.getResponseProperty("Name"); + this.accessAll = this.getResponseProperty("AccessAll"); + this.externalId = this.getResponseProperty("ExternalId"); + } +} + +export class GroupDetailsResponse extends GroupResponse { + collections: SelectionReadOnlyResponse[] = []; + + constructor(response: any) { + super(response); + const collections = this.getResponseProperty("Collections"); + if (collections != null) { + this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c)); + } + } +} diff --git a/jslib/common/src/models/response/identityCaptchaResponse.ts b/jslib/common/src/models/response/identityCaptchaResponse.ts new file mode 100644 index 00000000..2537057e --- /dev/null +++ b/jslib/common/src/models/response/identityCaptchaResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from "./baseResponse"; + +export class IdentityCaptchaResponse extends BaseResponse { + siteKey: string; + + constructor(response: any) { + super(response); + this.siteKey = this.getResponseProperty("HCaptcha_SiteKey"); + } +} diff --git a/jslib/common/src/models/response/identityTokenResponse.ts b/jslib/common/src/models/response/identityTokenResponse.ts new file mode 100644 index 00000000..87b5d396 --- /dev/null +++ b/jslib/common/src/models/response/identityTokenResponse.ts @@ -0,0 +1,38 @@ +import { KdfType } from "../../enums/kdfType"; + +import { BaseResponse } from "./baseResponse"; + +export class IdentityTokenResponse extends BaseResponse { + accessToken: string; + expiresIn: number; + refreshToken: string; + tokenType: string; + + resetMasterPassword: boolean; + privateKey: string; + key: string; + twoFactorToken: string; + kdf: KdfType; + kdfIterations: number; + forcePasswordReset: boolean; + apiUseKeyConnector: boolean; + keyConnectorUrl: string; + + constructor(response: any) { + super(response); + this.accessToken = response.access_token; + this.expiresIn = response.expires_in; + this.refreshToken = response.refresh_token; + this.tokenType = response.token_type; + + this.resetMasterPassword = this.getResponseProperty("ResetMasterPassword"); + this.privateKey = this.getResponseProperty("PrivateKey"); + this.key = this.getResponseProperty("Key"); + this.twoFactorToken = this.getResponseProperty("TwoFactorToken"); + this.kdf = this.getResponseProperty("Kdf"); + this.kdfIterations = this.getResponseProperty("KdfIterations"); + this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset"); + this.apiUseKeyConnector = this.getResponseProperty("ApiUseKeyConnector"); + this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); + } +} diff --git a/jslib/common/src/models/response/identityTwoFactorResponse.ts b/jslib/common/src/models/response/identityTwoFactorResponse.ts new file mode 100644 index 00000000..dc7dfde7 --- /dev/null +++ b/jslib/common/src/models/response/identityTwoFactorResponse.ts @@ -0,0 +1,24 @@ +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; + +import { BaseResponse } from "./baseResponse"; + +export class IdentityTwoFactorResponse extends BaseResponse { + twoFactorProviders: TwoFactorProviderType[]; + twoFactorProviders2 = new Map(); + captchaToken: string; + + constructor(response: any) { + super(response); + this.captchaToken = this.getResponseProperty("CaptchaBypassToken"); + this.twoFactorProviders = this.getResponseProperty("TwoFactorProviders"); + const twoFactorProviders2 = this.getResponseProperty("TwoFactorProviders2"); + if (twoFactorProviders2 != null) { + for (const prop in twoFactorProviders2) { + // eslint-disable-next-line + if (twoFactorProviders2.hasOwnProperty(prop)) { + this.twoFactorProviders2.set(parseInt(prop, null), twoFactorProviders2[prop]); + } + } + } + } +} diff --git a/jslib/common/src/models/response/keyConnectorUserKeyResponse.ts b/jslib/common/src/models/response/keyConnectorUserKeyResponse.ts new file mode 100644 index 00000000..5183397b --- /dev/null +++ b/jslib/common/src/models/response/keyConnectorUserKeyResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from "./baseResponse"; + +export class KeyConnectorUserKeyResponse extends BaseResponse { + key: string; + + constructor(response: any) { + super(response); + this.key = this.getResponseProperty("Key"); + } +} diff --git a/jslib/common/src/models/response/keysResponse.ts b/jslib/common/src/models/response/keysResponse.ts new file mode 100644 index 00000000..e1e8e5db --- /dev/null +++ b/jslib/common/src/models/response/keysResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class KeysResponse extends BaseResponse { + privateKey: string; + publicKey: string; + + constructor(response: any) { + super(response); + this.privateKey = this.getResponseProperty("PrivateKey"); + this.publicKey = this.getResponseProperty("PublicKey"); + } +} diff --git a/jslib/common/src/models/response/listResponse.ts b/jslib/common/src/models/response/listResponse.ts new file mode 100644 index 00000000..e37c6697 --- /dev/null +++ b/jslib/common/src/models/response/listResponse.ts @@ -0,0 +1,13 @@ +import { BaseResponse } from "./baseResponse"; + +export class ListResponse extends BaseResponse { + data: T[]; + continuationToken: string; + + constructor(response: any, t: new (dataResponse: any) => T) { + super(response); + const data = this.getResponseProperty("Data"); + this.data = data == null ? [] : data.map((dr: any) => new t(dr)); + this.continuationToken = this.getResponseProperty("ContinuationToken"); + } +} diff --git a/jslib/common/src/models/response/notificationResponse.ts b/jslib/common/src/models/response/notificationResponse.ts new file mode 100644 index 00000000..f23de8fe --- /dev/null +++ b/jslib/common/src/models/response/notificationResponse.ts @@ -0,0 +1,98 @@ +import { NotificationType } from "../../enums/notificationType"; + +import { BaseResponse } from "./baseResponse"; + +export class NotificationResponse extends BaseResponse { + contextId: string; + type: NotificationType; + payload: any; + + constructor(response: any) { + super(response); + this.contextId = this.getResponseProperty("ContextId"); + this.type = this.getResponseProperty("Type"); + + const payload = this.getResponseProperty("Payload"); + switch (this.type) { + case NotificationType.SyncCipherCreate: + case NotificationType.SyncCipherDelete: + case NotificationType.SyncCipherUpdate: + case NotificationType.SyncLoginDelete: + this.payload = new SyncCipherNotification(payload); + break; + case NotificationType.SyncFolderCreate: + case NotificationType.SyncFolderDelete: + case NotificationType.SyncFolderUpdate: + this.payload = new SyncFolderNotification(payload); + break; + case NotificationType.SyncVault: + case NotificationType.SyncCiphers: + case NotificationType.SyncOrgKeys: + case NotificationType.SyncSettings: + case NotificationType.LogOut: + this.payload = new UserNotification(payload); + break; + case NotificationType.SyncSendCreate: + case NotificationType.SyncSendUpdate: + case NotificationType.SyncSendDelete: + this.payload = new SyncSendNotification(payload); + break; + default: + break; + } + } +} + +export class SyncCipherNotification extends BaseResponse { + id: string; + userId: string; + organizationId: string; + collectionIds: string[]; + revisionDate: Date; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.collectionIds = this.getResponseProperty("CollectionIds"); + this.revisionDate = new Date(this.getResponseProperty("RevisionDate")); + } +} + +export class SyncFolderNotification extends BaseResponse { + id: string; + userId: string; + revisionDate: Date; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.revisionDate = new Date(this.getResponseProperty("RevisionDate")); + } +} + +export class UserNotification extends BaseResponse { + userId: string; + date: Date; + + constructor(response: any) { + super(response); + this.userId = this.getResponseProperty("UserId"); + this.date = new Date(this.getResponseProperty("Date")); + } +} + +export class SyncSendNotification extends BaseResponse { + id: string; + userId: string; + revisionDate: Date; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.revisionDate = new Date(this.getResponseProperty("RevisionDate")); + } +} diff --git a/jslib/common/src/models/response/organization/organizationSsoResponse.ts b/jslib/common/src/models/response/organization/organizationSsoResponse.ts new file mode 100644 index 00000000..abe6b720 --- /dev/null +++ b/jslib/common/src/models/response/organization/organizationSsoResponse.ts @@ -0,0 +1,35 @@ +import { SsoConfigApi } from "../../api/ssoConfigApi"; +import { BaseResponse } from "../baseResponse"; + +export class OrganizationSsoResponse extends BaseResponse { + enabled: boolean; + data: SsoConfigApi; + urls: SsoUrls; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.data = + this.getResponseProperty("Data") != null + ? new SsoConfigApi(this.getResponseProperty("Data")) + : null; + this.urls = new SsoUrls(this.getResponseProperty("Urls")); + } +} + +class SsoUrls extends BaseResponse { + callbackPath: string; + signedOutCallbackPath: string; + spEntityId: string; + spMetadataUrl: string; + spAcsUrl: string; + + constructor(response: any) { + super(response); + this.callbackPath = this.getResponseProperty("CallbackPath"); + this.signedOutCallbackPath = this.getResponseProperty("SignedOutCallbackPath"); + this.spEntityId = this.getResponseProperty("SpEntityId"); + this.spMetadataUrl = this.getResponseProperty("SpMetadataUrl"); + this.spAcsUrl = this.getResponseProperty("SpAcsUrl"); + } +} diff --git a/jslib/common/src/models/response/organizationAutoEnrollStatusResponse.ts b/jslib/common/src/models/response/organizationAutoEnrollStatusResponse.ts new file mode 100644 index 00000000..d960463e --- /dev/null +++ b/jslib/common/src/models/response/organizationAutoEnrollStatusResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class OrganizationAutoEnrollStatusResponse extends BaseResponse { + id: string; + resetPasswordEnabled: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.resetPasswordEnabled = this.getResponseProperty("ResetPasswordEnabled"); + } +} diff --git a/jslib/common/src/models/response/organizationKeysResponse.ts b/jslib/common/src/models/response/organizationKeysResponse.ts new file mode 100644 index 00000000..3d5c0d29 --- /dev/null +++ b/jslib/common/src/models/response/organizationKeysResponse.ts @@ -0,0 +1,7 @@ +import { KeysResponse } from "./keysResponse"; + +export class OrganizationKeysResponse extends KeysResponse { + constructor(response: any) { + super(response); + } +} diff --git a/jslib/common/src/models/response/organizationResponse.ts b/jslib/common/src/models/response/organizationResponse.ts new file mode 100644 index 00000000..addb2a28 --- /dev/null +++ b/jslib/common/src/models/response/organizationResponse.ts @@ -0,0 +1,60 @@ +import { PlanType } from "../../enums/planType"; + +import { BaseResponse } from "./baseResponse"; +import { PlanResponse } from "./planResponse"; + +export class OrganizationResponse extends BaseResponse { + id: string; + identifier: string; + name: string; + businessName: string; + businessAddress1: string; + businessAddress2: string; + businessAddress3: string; + businessCountry: string; + businessTaxNumber: string; + billingEmail: string; + plan: PlanResponse; + planType: PlanType; + seats: number; + maxAutoscaleSeats: number; + maxCollections: number; + maxStorageGb: number; + useGroups: boolean; + useDirectory: boolean; + useEvents: boolean; + useTotp: boolean; + use2fa: boolean; + useApi: boolean; + useResetPassword: boolean; + hasPublicAndPrivateKeys: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.identifier = this.getResponseProperty("Identifier"); + this.name = this.getResponseProperty("Name"); + this.businessName = this.getResponseProperty("BusinessName"); + this.businessAddress1 = this.getResponseProperty("BusinessAddress1"); + this.businessAddress2 = this.getResponseProperty("BusinessAddress2"); + this.businessAddress3 = this.getResponseProperty("BusinessAddress3"); + this.businessCountry = this.getResponseProperty("BusinessCountry"); + this.businessTaxNumber = this.getResponseProperty("BusinessTaxNumber"); + this.billingEmail = this.getResponseProperty("BillingEmail"); + const plan = this.getResponseProperty("Plan"); + this.plan = plan == null ? null : new PlanResponse(plan); + this.planType = this.getResponseProperty("PlanType"); + this.seats = this.getResponseProperty("Seats"); + this.maxAutoscaleSeats = this.getResponseProperty("MaxAutoscaleSeats"); + this.maxCollections = this.getResponseProperty("MaxCollections"); + this.maxStorageGb = this.getResponseProperty("MaxStorageGb"); + this.useGroups = this.getResponseProperty("UseGroups"); + this.useDirectory = this.getResponseProperty("UseDirectory"); + this.useEvents = this.getResponseProperty("UseEvents"); + this.useTotp = this.getResponseProperty("UseTotp"); + this.use2fa = this.getResponseProperty("Use2fa"); + this.useApi = this.getResponseProperty("UseApi"); + this.useResetPassword = this.getResponseProperty("UseResetPassword"); + this.hasPublicAndPrivateKeys = this.getResponseProperty("HasPublicAndPrivateKeys"); + } +} diff --git a/jslib/common/src/models/response/organizationSubscriptionResponse.ts b/jslib/common/src/models/response/organizationSubscriptionResponse.ts new file mode 100644 index 00000000..1985dc6e --- /dev/null +++ b/jslib/common/src/models/response/organizationSubscriptionResponse.ts @@ -0,0 +1,27 @@ +import { OrganizationResponse } from "./organizationResponse"; +import { + BillingSubscriptionResponse, + BillingSubscriptionUpcomingInvoiceResponse, +} from "./subscriptionResponse"; + +export class OrganizationSubscriptionResponse extends OrganizationResponse { + storageName: string; + storageGb: number; + subscription: BillingSubscriptionResponse; + upcomingInvoice: BillingSubscriptionUpcomingInvoiceResponse; + expiration: string; + + constructor(response: any) { + super(response); + this.storageName = this.getResponseProperty("StorageName"); + this.storageGb = this.getResponseProperty("StorageGb"); + const subscription = this.getResponseProperty("Subscription"); + this.subscription = subscription == null ? null : new BillingSubscriptionResponse(subscription); + const upcomingInvoice = this.getResponseProperty("UpcomingInvoice"); + this.upcomingInvoice = + upcomingInvoice == null + ? null + : new BillingSubscriptionUpcomingInvoiceResponse(upcomingInvoice); + this.expiration = this.getResponseProperty("Expiration"); + } +} diff --git a/jslib/common/src/models/response/organizationUserBulkPublicKeyResponse.ts b/jslib/common/src/models/response/organizationUserBulkPublicKeyResponse.ts new file mode 100644 index 00000000..1b77e036 --- /dev/null +++ b/jslib/common/src/models/response/organizationUserBulkPublicKeyResponse.ts @@ -0,0 +1,14 @@ +import { BaseResponse } from "./baseResponse"; + +export class OrganizationUserBulkPublicKeyResponse extends BaseResponse { + id: string; + userId: string; + key: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.key = this.getResponseProperty("Key"); + } +} diff --git a/jslib/common/src/models/response/organizationUserBulkResponse.ts b/jslib/common/src/models/response/organizationUserBulkResponse.ts new file mode 100644 index 00000000..15c6f371 --- /dev/null +++ b/jslib/common/src/models/response/organizationUserBulkResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class OrganizationUserBulkResponse extends BaseResponse { + id: string; + error: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.error = this.getResponseProperty("Error"); + } +} diff --git a/jslib/common/src/models/response/organizationUserResponse.ts b/jslib/common/src/models/response/organizationUserResponse.ts new file mode 100644 index 00000000..3e49375b --- /dev/null +++ b/jslib/common/src/models/response/organizationUserResponse.ts @@ -0,0 +1,70 @@ +import { KdfType } from "../../enums/kdfType"; +import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { BaseResponse } from "./baseResponse"; +import { SelectionReadOnlyResponse } from "./selectionReadOnlyResponse"; + +export class OrganizationUserResponse extends BaseResponse { + id: string; + userId: string; + type: OrganizationUserType; + status: OrganizationUserStatusType; + accessAll: boolean; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.type = this.getResponseProperty("Type"); + this.status = this.getResponseProperty("Status"); + this.permissions = new PermissionsApi(this.getResponseProperty("Permissions")); + this.accessAll = this.getResponseProperty("AccessAll"); + this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled"); + } +} + +export class OrganizationUserUserDetailsResponse extends OrganizationUserResponse { + name: string; + email: string; + twoFactorEnabled: boolean; + usesKeyConnector: boolean; + + constructor(response: any) { + super(response); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled"); + this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false; + } +} + +export class OrganizationUserDetailsResponse extends OrganizationUserResponse { + collections: SelectionReadOnlyResponse[] = []; + + constructor(response: any) { + super(response); + const collections = this.getResponseProperty("Collections"); + if (collections != null) { + this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c)); + } + } +} + +export class OrganizationUserResetPasswordDetailsReponse extends BaseResponse { + kdf: KdfType; + kdfIterations: number; + resetPasswordKey: string; + encryptedPrivateKey: string; + + constructor(response: any) { + super(response); + this.kdf = this.getResponseProperty("Kdf"); + this.kdfIterations = this.getResponseProperty("KdfIterations"); + this.resetPasswordKey = this.getResponseProperty("ResetPasswordKey"); + this.encryptedPrivateKey = this.getResponseProperty("EncryptedPrivateKey"); + } +} diff --git a/jslib/common/src/models/response/passwordHistoryResponse.ts b/jslib/common/src/models/response/passwordHistoryResponse.ts new file mode 100644 index 00000000..f3a8a133 --- /dev/null +++ b/jslib/common/src/models/response/passwordHistoryResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class PasswordHistoryResponse extends BaseResponse { + password: string; + lastUsedDate: string; + + constructor(response: any) { + super(response); + this.password = this.getResponseProperty("Password"); + this.lastUsedDate = this.getResponseProperty("LastUsedDate"); + } +} diff --git a/jslib/common/src/models/response/paymentResponse.ts b/jslib/common/src/models/response/paymentResponse.ts new file mode 100644 index 00000000..ec4977cd --- /dev/null +++ b/jslib/common/src/models/response/paymentResponse.ts @@ -0,0 +1,18 @@ +import { BaseResponse } from "./baseResponse"; +import { ProfileResponse } from "./profileResponse"; + +export class PaymentResponse extends BaseResponse { + userProfile: ProfileResponse; + paymentIntentClientSecret: string; + success: boolean; + + constructor(response: any) { + super(response); + const userProfile = this.getResponseProperty("UserProfile"); + if (userProfile != null) { + this.userProfile = new ProfileResponse(userProfile); + } + this.paymentIntentClientSecret = this.getResponseProperty("PaymentIntentClientSecret"); + this.success = this.getResponseProperty("Success"); + } +} diff --git a/jslib/common/src/models/response/planResponse.ts b/jslib/common/src/models/response/planResponse.ts new file mode 100644 index 00000000..04652542 --- /dev/null +++ b/jslib/common/src/models/response/planResponse.ts @@ -0,0 +1,95 @@ +import { PlanType } from "../../enums/planType"; +import { ProductType } from "../../enums/productType"; + +import { BaseResponse } from "./baseResponse"; + +export class PlanResponse extends BaseResponse { + type: PlanType; + product: ProductType; + name: string; + isAnnual: boolean; + nameLocalizationKey: string; + descriptionLocalizationKey: string; + canBeUsedByBusiness: boolean; + baseSeats: number; + baseStorageGb: number; + maxCollections: number; + maxUsers: number; + + hasAdditionalSeatsOption: boolean; + maxAdditionalSeats: number; + hasAdditionalStorageOption: boolean; + maxAdditionalStorage: number; + hasPremiumAccessOption: boolean; + trialPeriodDays: number; + + hasSelfHost: boolean; + hasPolicies: boolean; + hasGroups: boolean; + hasDirectory: boolean; + hasEvents: boolean; + hasTotp: boolean; + has2fa: boolean; + hasApi: boolean; + hasSso: boolean; + hasResetPassword: boolean; + usersGetPremium: boolean; + + upgradeSortOrder: number; + displaySortOrder: number; + legacyYear: number; + disabled: boolean; + + stripePlanId: string; + stripeSeatPlanId: string; + stripeStoragePlanId: string; + stripePremiumAccessPlanId: string; + basePrice: number; + seatPrice: number; + additionalStoragePricePerGb: number; + premiumAccessOptionPrice: number; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + this.product = this.getResponseProperty("Product"); + this.name = this.getResponseProperty("Name"); + this.isAnnual = this.getResponseProperty("IsAnnual"); + this.nameLocalizationKey = this.getResponseProperty("NameLocalizationKey"); + this.descriptionLocalizationKey = this.getResponseProperty("DescriptionLocalizationKey"); + this.canBeUsedByBusiness = this.getResponseProperty("CanBeUsedByBusiness"); + this.baseSeats = this.getResponseProperty("BaseSeats"); + this.baseStorageGb = this.getResponseProperty("BaseStorageGb"); + this.maxCollections = this.getResponseProperty("MaxCollections"); + this.maxUsers = this.getResponseProperty("MaxUsers"); + this.hasAdditionalSeatsOption = this.getResponseProperty("HasAdditionalSeatsOption"); + this.maxAdditionalSeats = this.getResponseProperty("MaxAdditionalSeats"); + this.hasAdditionalStorageOption = this.getResponseProperty("HasAdditionalStorageOption"); + this.maxAdditionalStorage = this.getResponseProperty("MaxAdditionalStorage"); + this.hasPremiumAccessOption = this.getResponseProperty("HasPremiumAccessOption"); + this.trialPeriodDays = this.getResponseProperty("TrialPeriodDays"); + this.hasSelfHost = this.getResponseProperty("HasSelfHost"); + this.hasPolicies = this.getResponseProperty("HasPolicies"); + this.hasGroups = this.getResponseProperty("HasGroups"); + this.hasDirectory = this.getResponseProperty("HasDirectory"); + this.hasEvents = this.getResponseProperty("HasEvents"); + this.hasTotp = this.getResponseProperty("HasTotp"); + this.has2fa = this.getResponseProperty("Has2fa"); + this.hasApi = this.getResponseProperty("HasApi"); + this.hasSso = this.getResponseProperty("HasSso"); + this.hasResetPassword = this.getResponseProperty("HasResetPassword"); + this.usersGetPremium = this.getResponseProperty("UsersGetPremium"); + this.upgradeSortOrder = this.getResponseProperty("UpgradeSortOrder"); + this.displaySortOrder = this.getResponseProperty("SortOrder"); + this.legacyYear = this.getResponseProperty("LegacyYear"); + this.disabled = this.getResponseProperty("Disabled"); + this.stripePlanId = this.getResponseProperty("StripePlanId"); + this.stripeSeatPlanId = this.getResponseProperty("StripeSeatPlanId"); + this.stripeStoragePlanId = this.getResponseProperty("StripeStoragePlanId"); + this.stripePremiumAccessPlanId = this.getResponseProperty("StripePremiumAccessPlanId"); + this.basePrice = this.getResponseProperty("BasePrice"); + this.seatPrice = this.getResponseProperty("SeatPrice"); + this.additionalStoragePricePerGb = this.getResponseProperty("AdditionalStoragePricePerGb"); + this.premiumAccessOptionPrice = this.getResponseProperty("PremiumAccessOptionPrice"); + } +} diff --git a/jslib/common/src/models/response/policyResponse.ts b/jslib/common/src/models/response/policyResponse.ts new file mode 100644 index 00000000..d38375ef --- /dev/null +++ b/jslib/common/src/models/response/policyResponse.ts @@ -0,0 +1,20 @@ +import { PolicyType } from "../../enums/policyType"; + +import { BaseResponse } from "./baseResponse"; + +export class PolicyResponse extends BaseResponse { + id: string; + organizationId: string; + type: PolicyType; + data: any; + enabled: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.type = this.getResponseProperty("Type"); + this.data = this.getResponseProperty("Data"); + this.enabled = this.getResponseProperty("Enabled"); + } +} diff --git a/jslib/common/src/models/response/preloginResponse.ts b/jslib/common/src/models/response/preloginResponse.ts new file mode 100644 index 00000000..7756f8ed --- /dev/null +++ b/jslib/common/src/models/response/preloginResponse.ts @@ -0,0 +1,14 @@ +import { KdfType } from "../../enums/kdfType"; + +import { BaseResponse } from "./baseResponse"; + +export class PreloginResponse extends BaseResponse { + kdf: KdfType; + kdfIterations: number; + + constructor(response: any) { + super(response); + this.kdf = this.getResponseProperty("Kdf"); + this.kdfIterations = this.getResponseProperty("KdfIterations"); + } +} diff --git a/jslib/common/src/models/response/profileOrganizationResponse.ts b/jslib/common/src/models/response/profileOrganizationResponse.ts new file mode 100644 index 00000000..9c992e54 --- /dev/null +++ b/jslib/common/src/models/response/profileOrganizationResponse.ts @@ -0,0 +1,81 @@ +import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { ProductType } from "../../enums/productType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { BaseResponse } from "./baseResponse"; + +export class ProfileOrganizationResponse extends BaseResponse { + id: string; + name: string; + usePolicies: boolean; + useGroups: boolean; + useDirectory: boolean; + useEvents: boolean; + useTotp: boolean; + use2fa: boolean; + useApi: boolean; + useSso: boolean; + useKeyConnector: boolean; + useResetPassword: boolean; + selfHost: boolean; + usersGetPremium: boolean; + seats: number; + maxCollections: number; + maxStorageGb?: number; + key: string; + hasPublicAndPrivateKeys: boolean; + status: OrganizationUserStatusType; + type: OrganizationUserType; + enabled: boolean; + ssoBound: boolean; + identifier: string; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + userId: string; + providerId: string; + providerName: string; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.usePolicies = this.getResponseProperty("UsePolicies"); + this.useGroups = this.getResponseProperty("UseGroups"); + this.useDirectory = this.getResponseProperty("UseDirectory"); + this.useEvents = this.getResponseProperty("UseEvents"); + this.useTotp = this.getResponseProperty("UseTotp"); + this.use2fa = this.getResponseProperty("Use2fa"); + this.useApi = this.getResponseProperty("UseApi"); + this.useSso = this.getResponseProperty("UseSso"); + this.useKeyConnector = this.getResponseProperty("UseKeyConnector") ?? false; + this.useResetPassword = this.getResponseProperty("UseResetPassword"); + this.selfHost = this.getResponseProperty("SelfHost"); + this.usersGetPremium = this.getResponseProperty("UsersGetPremium"); + this.seats = this.getResponseProperty("Seats"); + this.maxCollections = this.getResponseProperty("MaxCollections"); + this.maxStorageGb = this.getResponseProperty("MaxStorageGb"); + this.key = this.getResponseProperty("Key"); + this.hasPublicAndPrivateKeys = this.getResponseProperty("HasPublicAndPrivateKeys"); + this.status = this.getResponseProperty("Status"); + this.type = this.getResponseProperty("Type"); + this.enabled = this.getResponseProperty("Enabled"); + this.ssoBound = this.getResponseProperty("SsoBound"); + this.identifier = this.getResponseProperty("Identifier"); + this.permissions = new PermissionsApi(this.getResponseProperty("permissions")); + this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled"); + this.userId = this.getResponseProperty("UserId"); + this.providerId = this.getResponseProperty("ProviderId"); + this.providerName = this.getResponseProperty("ProviderName"); + this.familySponsorshipFriendlyName = this.getResponseProperty("FamilySponsorshipFriendlyName"); + this.familySponsorshipAvailable = this.getResponseProperty("FamilySponsorshipAvailable"); + this.planProductType = this.getResponseProperty("PlanProductType"); + this.keyConnectorEnabled = this.getResponseProperty("KeyConnectorEnabled") ?? false; + this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); + } +} diff --git a/jslib/common/src/models/response/profileProviderOrganizationResponse.ts b/jslib/common/src/models/response/profileProviderOrganizationResponse.ts new file mode 100644 index 00000000..74b74c00 --- /dev/null +++ b/jslib/common/src/models/response/profileProviderOrganizationResponse.ts @@ -0,0 +1,8 @@ +import { ProfileOrganizationResponse } from "./profileOrganizationResponse"; + +export class ProfileProviderOrganizationResponse extends ProfileOrganizationResponse { + constructor(response: any) { + super(response); + this.keyConnectorEnabled = false; + } +} diff --git a/jslib/common/src/models/response/profileProviderResponse.ts b/jslib/common/src/models/response/profileProviderResponse.ts new file mode 100644 index 00000000..84e8bbf6 --- /dev/null +++ b/jslib/common/src/models/response/profileProviderResponse.ts @@ -0,0 +1,30 @@ +import { ProviderUserStatusType } from "../../enums/providerUserStatusType"; +import { ProviderUserType } from "../../enums/providerUserType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { BaseResponse } from "./baseResponse"; + +export class ProfileProviderResponse extends BaseResponse { + id: string; + name: string; + key: string; + status: ProviderUserStatusType; + type: ProviderUserType; + enabled: boolean; + permissions: PermissionsApi; + userId: string; + useEvents: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.key = this.getResponseProperty("Key"); + this.status = this.getResponseProperty("Status"); + this.type = this.getResponseProperty("Type"); + this.enabled = this.getResponseProperty("Enabled"); + this.permissions = new PermissionsApi(this.getResponseProperty("permissions")); + this.userId = this.getResponseProperty("UserId"); + this.useEvents = this.getResponseProperty("UseEvents"); + } +} diff --git a/jslib/common/src/models/response/profileResponse.ts b/jslib/common/src/models/response/profileResponse.ts new file mode 100644 index 00000000..2c3e66fb --- /dev/null +++ b/jslib/common/src/models/response/profileResponse.ts @@ -0,0 +1,55 @@ +import { BaseResponse } from "./baseResponse"; +import { ProfileOrganizationResponse } from "./profileOrganizationResponse"; +import { ProfileProviderOrganizationResponse } from "./profileProviderOrganizationResponse"; +import { ProfileProviderResponse } from "./profileProviderResponse"; + +export class ProfileResponse extends BaseResponse { + id: string; + name: string; + email: string; + emailVerified: boolean; + masterPasswordHint: string; + premium: boolean; + culture: string; + twoFactorEnabled: boolean; + key: string; + privateKey: string; + securityStamp: string; + forcePasswordReset: boolean; + usesKeyConnector: boolean; + organizations: ProfileOrganizationResponse[] = []; + providers: ProfileProviderResponse[] = []; + providerOrganizations: ProfileProviderOrganizationResponse[] = []; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + this.emailVerified = this.getResponseProperty("EmailVerified"); + this.masterPasswordHint = this.getResponseProperty("MasterPasswordHint"); + this.premium = this.getResponseProperty("Premium"); + this.culture = this.getResponseProperty("Culture"); + this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled"); + this.key = this.getResponseProperty("Key"); + this.privateKey = this.getResponseProperty("PrivateKey"); + this.securityStamp = this.getResponseProperty("SecurityStamp"); + this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset") ?? false; + this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false; + + const organizations = this.getResponseProperty("Organizations"); + if (organizations != null) { + this.organizations = organizations.map((o: any) => new ProfileOrganizationResponse(o)); + } + const providers = this.getResponseProperty("Providers"); + if (providers != null) { + this.providers = providers.map((o: any) => new ProfileProviderResponse(o)); + } + const providerOrganizations = this.getResponseProperty("ProviderOrganizations"); + if (providerOrganizations != null) { + this.providerOrganizations = providerOrganizations.map( + (o: any) => new ProfileProviderOrganizationResponse(o) + ); + } + } +} diff --git a/jslib/common/src/models/response/provider/providerOrganizationResponse.ts b/jslib/common/src/models/response/provider/providerOrganizationResponse.ts new file mode 100644 index 00000000..d733362a --- /dev/null +++ b/jslib/common/src/models/response/provider/providerOrganizationResponse.ts @@ -0,0 +1,31 @@ +import { BaseResponse } from "../baseResponse"; + +export class ProviderOrganizationResponse extends BaseResponse { + id: string; + providerId: string; + organizationId: string; + key: string; + settings: string; + creationDate: string; + revisionDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.providerId = this.getResponseProperty("ProviderId"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.key = this.getResponseProperty("Key"); + this.settings = this.getResponseProperty("Settings"); + this.creationDate = this.getResponseProperty("CreationDate"); + this.revisionDate = this.getResponseProperty("RevisionDate"); + } +} + +export class ProviderOrganizationOrganizationDetailsResponse extends ProviderOrganizationResponse { + organizationName: string; + + constructor(response: any) { + super(response); + this.organizationName = this.getResponseProperty("OrganizationName"); + } +} diff --git a/jslib/common/src/models/response/provider/providerResponse.ts b/jslib/common/src/models/response/provider/providerResponse.ts new file mode 100644 index 00000000..2b8729fa --- /dev/null +++ b/jslib/common/src/models/response/provider/providerResponse.ts @@ -0,0 +1,16 @@ +import { BaseResponse } from "../baseResponse"; + +export class ProviderResponse extends BaseResponse { + id: string; + name: string; + businessName: string; + billingEmail: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.businessName = this.getResponseProperty("BusinessName"); + this.billingEmail = this.getResponseProperty("BillingEmail"); + } +} diff --git a/jslib/common/src/models/response/provider/providerUserBulkPublicKeyResponse.ts b/jslib/common/src/models/response/provider/providerUserBulkPublicKeyResponse.ts new file mode 100644 index 00000000..ad078f98 --- /dev/null +++ b/jslib/common/src/models/response/provider/providerUserBulkPublicKeyResponse.ts @@ -0,0 +1,3 @@ +import { OrganizationUserBulkPublicKeyResponse } from "../organizationUserBulkPublicKeyResponse"; + +export class ProviderUserBulkPublicKeyResponse extends OrganizationUserBulkPublicKeyResponse {} diff --git a/jslib/common/src/models/response/provider/providerUserBulkResponse.ts b/jslib/common/src/models/response/provider/providerUserBulkResponse.ts new file mode 100644 index 00000000..6cf12f96 --- /dev/null +++ b/jslib/common/src/models/response/provider/providerUserBulkResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "../baseResponse"; + +export class ProviderUserBulkResponse extends BaseResponse { + id: string; + error: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.error = this.getResponseProperty("Error"); + } +} diff --git a/jslib/common/src/models/response/provider/providerUserResponse.ts b/jslib/common/src/models/response/provider/providerUserResponse.ts new file mode 100644 index 00000000..cf1a181c --- /dev/null +++ b/jslib/common/src/models/response/provider/providerUserResponse.ts @@ -0,0 +1,32 @@ +import { ProviderUserStatusType } from "../../../enums/providerUserStatusType"; +import { ProviderUserType } from "../../../enums/providerUserType"; +import { PermissionsApi } from "../../api/permissionsApi"; +import { BaseResponse } from "../baseResponse"; + +export class ProviderUserResponse extends BaseResponse { + id: string; + userId: string; + type: ProviderUserType; + status: ProviderUserStatusType; + permissions: PermissionsApi; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.type = this.getResponseProperty("Type"); + this.status = this.getResponseProperty("Status"); + this.permissions = new PermissionsApi(this.getResponseProperty("Permissions")); + } +} + +export class ProviderUserUserDetailsResponse extends ProviderUserResponse { + name: string; + email: string; + + constructor(response: any) { + super(response); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + } +} diff --git a/jslib/common/src/models/response/selectionReadOnlyResponse.ts b/jslib/common/src/models/response/selectionReadOnlyResponse.ts new file mode 100644 index 00000000..cee9361c --- /dev/null +++ b/jslib/common/src/models/response/selectionReadOnlyResponse.ts @@ -0,0 +1,14 @@ +import { BaseResponse } from "./baseResponse"; + +export class SelectionReadOnlyResponse extends BaseResponse { + id: string; + readOnly: boolean; + hidePasswords: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.readOnly = this.getResponseProperty("ReadOnly"); + this.hidePasswords = this.getResponseProperty("HidePasswords"); + } +} diff --git a/jslib/common/src/models/response/sendAccessResponse.ts b/jslib/common/src/models/response/sendAccessResponse.ts new file mode 100644 index 00000000..e98a9d05 --- /dev/null +++ b/jslib/common/src/models/response/sendAccessResponse.ts @@ -0,0 +1,35 @@ +import { SendType } from "../../enums/sendType"; +import { SendFileApi } from "../api/sendFileApi"; +import { SendTextApi } from "../api/sendTextApi"; + +import { BaseResponse } from "./baseResponse"; + +export class SendAccessResponse extends BaseResponse { + id: string; + type: SendType; + name: string; + file: SendFileApi; + text: SendTextApi; + expirationDate: Date; + creatorIdentifier: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.type = this.getResponseProperty("Type"); + this.name = this.getResponseProperty("Name"); + + const text = this.getResponseProperty("Text"); + if (text != null) { + this.text = new SendTextApi(text); + } + + const file = this.getResponseProperty("File"); + if (file != null) { + this.file = new SendFileApi(file); + } + + this.expirationDate = this.getResponseProperty("ExpirationDate"); + this.creatorIdentifier = this.getResponseProperty("CreatorIdentifier"); + } +} diff --git a/jslib/common/src/models/response/sendFileDownloadDataResponse.ts b/jslib/common/src/models/response/sendFileDownloadDataResponse.ts new file mode 100644 index 00000000..ca2575a2 --- /dev/null +++ b/jslib/common/src/models/response/sendFileDownloadDataResponse.ts @@ -0,0 +1,11 @@ +import { BaseResponse } from "./baseResponse"; + +export class SendFileDownloadDataResponse extends BaseResponse { + id: string = null; + url: string = null; + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.url = this.getResponseProperty("Url"); + } +} diff --git a/jslib/common/src/models/response/sendFileUploadDataResponse.ts b/jslib/common/src/models/response/sendFileUploadDataResponse.ts new file mode 100644 index 00000000..b787f2cc --- /dev/null +++ b/jslib/common/src/models/response/sendFileUploadDataResponse.ts @@ -0,0 +1,17 @@ +import { FileUploadType } from "../../enums/fileUploadType"; + +import { BaseResponse } from "./baseResponse"; +import { SendResponse } from "./sendResponse"; + +export class SendFileUploadDataResponse extends BaseResponse { + fileUploadType: FileUploadType; + sendResponse: SendResponse; + url: string = null; + constructor(response: any) { + super(response); + this.fileUploadType = this.getResponseProperty("FileUploadType"); + const sendResponse = this.getResponseProperty("SendResponse"); + this.sendResponse = sendResponse == null ? null : new SendResponse(sendResponse); + this.url = this.getResponseProperty("Url"); + } +} diff --git a/jslib/common/src/models/response/sendResponse.ts b/jslib/common/src/models/response/sendResponse.ts new file mode 100644 index 00000000..28750725 --- /dev/null +++ b/jslib/common/src/models/response/sendResponse.ts @@ -0,0 +1,52 @@ +import { SendType } from "../../enums/sendType"; +import { SendFileApi } from "../api/sendFileApi"; +import { SendTextApi } from "../api/sendTextApi"; + +import { BaseResponse } from "./baseResponse"; + +export class SendResponse extends BaseResponse { + id: string; + accessId: string; + type: SendType; + name: string; + notes: string; + file: SendFileApi; + text: SendTextApi; + key: string; + maxAccessCount?: number; + accessCount: number; + revisionDate: string; + expirationDate: string; + deletionDate: string; + password: string; + disable: boolean; + hideEmail: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.accessId = this.getResponseProperty("AccessId"); + this.type = this.getResponseProperty("Type"); + this.name = this.getResponseProperty("Name"); + this.notes = this.getResponseProperty("Notes"); + this.key = this.getResponseProperty("Key"); + this.maxAccessCount = this.getResponseProperty("MaxAccessCount"); + this.accessCount = this.getResponseProperty("AccessCount"); + this.revisionDate = this.getResponseProperty("RevisionDate"); + this.expirationDate = this.getResponseProperty("ExpirationDate"); + this.deletionDate = this.getResponseProperty("DeletionDate"); + this.password = this.getResponseProperty("Password"); + this.disable = this.getResponseProperty("Disabled") || false; + this.hideEmail = this.getResponseProperty("HideEmail") || false; + + const text = this.getResponseProperty("Text"); + if (text != null) { + this.text = new SendTextApi(text); + } + + const file = this.getResponseProperty("File"); + if (file != null) { + this.file = new SendFileApi(file); + } + } +} diff --git a/jslib/common/src/models/response/subscriptionResponse.ts b/jslib/common/src/models/response/subscriptionResponse.ts new file mode 100644 index 00000000..fc5570c3 --- /dev/null +++ b/jslib/common/src/models/response/subscriptionResponse.ts @@ -0,0 +1,85 @@ +import { BaseResponse } from "./baseResponse"; + +export class SubscriptionResponse extends BaseResponse { + storageName: string; + storageGb: number; + maxStorageGb: number; + subscription: BillingSubscriptionResponse; + upcomingInvoice: BillingSubscriptionUpcomingInvoiceResponse; + license: any; + expiration: string; + usingInAppPurchase: boolean; + + constructor(response: any) { + super(response); + this.storageName = this.getResponseProperty("StorageName"); + this.storageGb = this.getResponseProperty("StorageGb"); + this.maxStorageGb = this.getResponseProperty("MaxStorageGb"); + this.license = this.getResponseProperty("License"); + this.expiration = this.getResponseProperty("Expiration"); + this.usingInAppPurchase = this.getResponseProperty("UsingInAppPurchase"); + const subscription = this.getResponseProperty("Subscription"); + const upcomingInvoice = this.getResponseProperty("UpcomingInvoice"); + this.subscription = subscription == null ? null : new BillingSubscriptionResponse(subscription); + this.upcomingInvoice = + upcomingInvoice == null + ? null + : new BillingSubscriptionUpcomingInvoiceResponse(upcomingInvoice); + } +} + +export class BillingSubscriptionResponse extends BaseResponse { + trialStartDate: string; + trialEndDate: string; + periodStartDate: string; + periodEndDate: string; + cancelledDate: string; + cancelAtEndDate: boolean; + status: string; + cancelled: boolean; + items: BillingSubscriptionItemResponse[] = []; + + constructor(response: any) { + super(response); + this.trialEndDate = this.getResponseProperty("TrialStartDate"); + this.trialEndDate = this.getResponseProperty("TrialEndDate"); + this.periodStartDate = this.getResponseProperty("PeriodStartDate"); + this.periodEndDate = this.getResponseProperty("PeriodEndDate"); + this.cancelledDate = this.getResponseProperty("CancelledDate"); + this.cancelAtEndDate = this.getResponseProperty("CancelAtEndDate"); + this.status = this.getResponseProperty("Status"); + this.cancelled = this.getResponseProperty("Cancelled"); + const items = this.getResponseProperty("Items"); + if (items != null) { + this.items = items.map((i: any) => new BillingSubscriptionItemResponse(i)); + } + } +} + +export class BillingSubscriptionItemResponse extends BaseResponse { + name: string; + amount: number; + quantity: number; + interval: string; + sponsoredSubscriptionItem: boolean; + + constructor(response: any) { + super(response); + this.name = this.getResponseProperty("Name"); + this.amount = this.getResponseProperty("Amount"); + this.quantity = this.getResponseProperty("Quantity"); + this.interval = this.getResponseProperty("Interval"); + this.sponsoredSubscriptionItem = this.getResponseProperty("SponsoredSubscriptionItem"); + } +} + +export class BillingSubscriptionUpcomingInvoiceResponse extends BaseResponse { + date: string; + amount: number; + + constructor(response: any) { + super(response); + this.date = this.getResponseProperty("Date"); + this.amount = this.getResponseProperty("Amount"); + } +} diff --git a/jslib/common/src/models/response/syncResponse.ts b/jslib/common/src/models/response/syncResponse.ts new file mode 100644 index 00000000..cab3ff2e --- /dev/null +++ b/jslib/common/src/models/response/syncResponse.ts @@ -0,0 +1,57 @@ +import { BaseResponse } from "./baseResponse"; +import { CipherResponse } from "./cipherResponse"; +import { CollectionDetailsResponse } from "./collectionResponse"; +import { DomainsResponse } from "./domainsResponse"; +import { FolderResponse } from "./folderResponse"; +import { PolicyResponse } from "./policyResponse"; +import { ProfileResponse } from "./profileResponse"; +import { SendResponse } from "./sendResponse"; + +export class SyncResponse extends BaseResponse { + profile?: ProfileResponse; + folders: FolderResponse[] = []; + collections: CollectionDetailsResponse[] = []; + ciphers: CipherResponse[] = []; + domains?: DomainsResponse; + policies?: PolicyResponse[] = []; + sends: SendResponse[] = []; + + constructor(response: any) { + super(response); + + const profile = this.getResponseProperty("Profile"); + if (profile != null) { + this.profile = new ProfileResponse(profile); + } + + const folders = this.getResponseProperty("Folders"); + if (folders != null) { + this.folders = folders.map((f: any) => new FolderResponse(f)); + } + + const collections = this.getResponseProperty("Collections"); + if (collections != null) { + this.collections = collections.map((c: any) => new CollectionDetailsResponse(c)); + } + + const ciphers = this.getResponseProperty("Ciphers"); + if (ciphers != null) { + this.ciphers = ciphers.map((c: any) => new CipherResponse(c)); + } + + const domains = this.getResponseProperty("Domains"); + if (domains != null) { + this.domains = new DomainsResponse(domains); + } + + const policies = this.getResponseProperty("Policies"); + if (policies != null) { + this.policies = policies.map((p: any) => new PolicyResponse(p)); + } + + const sends = this.getResponseProperty("Sends"); + if (sends != null) { + this.sends = sends.map((s: any) => new SendResponse(s)); + } + } +} diff --git a/jslib/common/src/models/response/taxInfoResponse.ts b/jslib/common/src/models/response/taxInfoResponse.ts new file mode 100644 index 00000000..bb4693ca --- /dev/null +++ b/jslib/common/src/models/response/taxInfoResponse.ts @@ -0,0 +1,24 @@ +import { BaseResponse } from "./baseResponse"; + +export class TaxInfoResponse extends BaseResponse { + taxId: string; + taxIdType: string; + line1: string; + line2: string; + city: string; + state: string; + country: string; + postalCode: string; + + constructor(response: any) { + super(response); + this.taxId = this.getResponseProperty("TaxIdNumber"); + this.taxIdType = this.getResponseProperty("TaxIdType"); + this.line1 = this.getResponseProperty("Line1"); + this.line2 = this.getResponseProperty("Line2"); + this.city = this.getResponseProperty("City"); + this.state = this.getResponseProperty("State"); + this.postalCode = this.getResponseProperty("PostalCode"); + this.country = this.getResponseProperty("Country"); + } +} diff --git a/jslib/common/src/models/response/taxRateResponse.ts b/jslib/common/src/models/response/taxRateResponse.ts new file mode 100644 index 00000000..28274a50 --- /dev/null +++ b/jslib/common/src/models/response/taxRateResponse.ts @@ -0,0 +1,18 @@ +import { BaseResponse } from "./baseResponse"; + +export class TaxRateResponse extends BaseResponse { + id: string; + country: string; + state: string; + postalCode: string; + rate: number; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.country = this.getResponseProperty("Country"); + this.state = this.getResponseProperty("State"); + this.postalCode = this.getResponseProperty("PostalCode"); + this.rate = this.getResponseProperty("Rate"); + } +} diff --git a/jslib/common/src/models/response/twoFactorAuthenticatorResponse.ts b/jslib/common/src/models/response/twoFactorAuthenticatorResponse.ts new file mode 100644 index 00000000..8eb73eaa --- /dev/null +++ b/jslib/common/src/models/response/twoFactorAuthenticatorResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorAuthenticatorResponse extends BaseResponse { + enabled: boolean; + key: string; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.key = this.getResponseProperty("Key"); + } +} diff --git a/jslib/common/src/models/response/twoFactorDuoResponse.ts b/jslib/common/src/models/response/twoFactorDuoResponse.ts new file mode 100644 index 00000000..401de796 --- /dev/null +++ b/jslib/common/src/models/response/twoFactorDuoResponse.ts @@ -0,0 +1,16 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorDuoResponse extends BaseResponse { + enabled: boolean; + host: string; + secretKey: string; + integrationKey: string; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.host = this.getResponseProperty("Host"); + this.secretKey = this.getResponseProperty("SecretKey"); + this.integrationKey = this.getResponseProperty("IntegrationKey"); + } +} diff --git a/jslib/common/src/models/response/twoFactorEmailResponse.ts b/jslib/common/src/models/response/twoFactorEmailResponse.ts new file mode 100644 index 00000000..ad1936d3 --- /dev/null +++ b/jslib/common/src/models/response/twoFactorEmailResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorEmailResponse extends BaseResponse { + enabled: boolean; + email: string; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.email = this.getResponseProperty("Email"); + } +} diff --git a/jslib/common/src/models/response/twoFactorProviderResponse.ts b/jslib/common/src/models/response/twoFactorProviderResponse.ts new file mode 100644 index 00000000..4ccd86cb --- /dev/null +++ b/jslib/common/src/models/response/twoFactorProviderResponse.ts @@ -0,0 +1,14 @@ +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; + +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorProviderResponse extends BaseResponse { + enabled: boolean; + type: TwoFactorProviderType; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.type = this.getResponseProperty("Type"); + } +} diff --git a/jslib/common/src/models/response/twoFactorRescoverResponse.ts b/jslib/common/src/models/response/twoFactorRescoverResponse.ts new file mode 100644 index 00000000..0e26db9b --- /dev/null +++ b/jslib/common/src/models/response/twoFactorRescoverResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorRecoverResponse extends BaseResponse { + code: string; + + constructor(response: any) { + super(response); + this.code = this.getResponseProperty("Code"); + } +} diff --git a/jslib/common/src/models/response/twoFactorWebAuthnResponse.ts b/jslib/common/src/models/response/twoFactorWebAuthnResponse.ts new file mode 100644 index 00000000..21ab66a0 --- /dev/null +++ b/jslib/common/src/models/response/twoFactorWebAuthnResponse.ts @@ -0,0 +1,60 @@ +import { Utils } from "../../misc/utils"; + +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorWebAuthnResponse extends BaseResponse { + enabled: boolean; + keys: KeyResponse[]; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + const keys = this.getResponseProperty("Keys"); + this.keys = keys == null ? null : keys.map((k: any) => new KeyResponse(k)); + } +} + +export class KeyResponse extends BaseResponse { + name: string; + id: number; + migrated: boolean; + + constructor(response: any) { + super(response); + this.name = this.getResponseProperty("Name"); + this.id = this.getResponseProperty("Id"); + this.migrated = this.getResponseProperty("Migrated"); + } +} + +export class ChallengeResponse extends BaseResponse implements PublicKeyCredentialCreationOptions { + attestation?: AttestationConveyancePreference; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: BufferSource; + excludeCredentials?: PublicKeyCredentialDescriptor[]; + extensions?: AuthenticationExtensionsClientInputs; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntity; + + constructor(response: any) { + super(response); + this.attestation = this.getResponseProperty("attestation"); + this.authenticatorSelection = this.getResponseProperty("authenticatorSelection"); + this.challenge = Utils.fromUrlB64ToArray(this.getResponseProperty("challenge")); + this.excludeCredentials = this.getResponseProperty("excludeCredentials").map((c: any) => { + c.id = Utils.fromUrlB64ToArray(c.id).buffer; + return c; + }); + this.extensions = this.getResponseProperty("extensions"); + this.pubKeyCredParams = this.getResponseProperty("pubKeyCredParams"); + this.rp = this.getResponseProperty("rp"); + this.timeout = this.getResponseProperty("timeout"); + + const user = this.getResponseProperty("user"); + user.id = Utils.fromUrlB64ToArray(user.id); + + this.user = user; + } +} diff --git a/jslib/common/src/models/response/twoFactorYubiKeyResponse.ts b/jslib/common/src/models/response/twoFactorYubiKeyResponse.ts new file mode 100644 index 00000000..ee75074b --- /dev/null +++ b/jslib/common/src/models/response/twoFactorYubiKeyResponse.ts @@ -0,0 +1,22 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorYubiKeyResponse extends BaseResponse { + enabled: boolean; + key1: string; + key2: string; + key3: string; + key4: string; + key5: string; + nfc: boolean; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.key1 = this.getResponseProperty("Key1"); + this.key2 = this.getResponseProperty("Key2"); + this.key3 = this.getResponseProperty("Key3"); + this.key4 = this.getResponseProperty("Key4"); + this.key5 = this.getResponseProperty("Key5"); + this.nfc = this.getResponseProperty("Nfc"); + } +} diff --git a/jslib/common/src/models/response/userKeyResponse.ts b/jslib/common/src/models/response/userKeyResponse.ts new file mode 100644 index 00000000..5550abdf --- /dev/null +++ b/jslib/common/src/models/response/userKeyResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class UserKeyResponse extends BaseResponse { + userId: string; + publicKey: string; + + constructor(response: any) { + super(response); + this.userId = this.getResponseProperty("UserId"); + this.publicKey = this.getResponseProperty("PublicKey"); + } +} diff --git a/jslib/common/src/models/view/attachmentView.ts b/jslib/common/src/models/view/attachmentView.ts new file mode 100644 index 00000000..4ebbec7e --- /dev/null +++ b/jslib/common/src/models/view/attachmentView.ts @@ -0,0 +1,35 @@ +import { Attachment } from "../domain/attachment"; +import { SymmetricCryptoKey } from "../domain/symmetricCryptoKey"; + +import { View } from "./view"; + +export class AttachmentView implements View { + id: string = null; + url: string = null; + size: string = null; + sizeName: string = null; + fileName: string = null; + key: SymmetricCryptoKey = null; + + constructor(a?: Attachment) { + if (!a) { + return; + } + + this.id = a.id; + this.url = a.url; + this.size = a.size; + this.sizeName = a.sizeName; + } + + get fileSize(): number { + try { + if (this.size != null) { + return parseInt(this.size, null); + } + } catch { + // Invalid file size. + } + return 0; + } +} diff --git a/jslib/common/src/models/view/cardView.ts b/jslib/common/src/models/view/cardView.ts new file mode 100644 index 00000000..769fa440 --- /dev/null +++ b/jslib/common/src/models/view/cardView.ts @@ -0,0 +1,82 @@ +import { CardLinkedId as LinkedId } from "../../enums/linkedIdType"; +import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator"; + +import { ItemView } from "./itemView"; + +export class CardView extends ItemView { + @linkedFieldOption(LinkedId.CardholderName) + cardholderName: string = null; + @linkedFieldOption(LinkedId.ExpMonth, "expirationMonth") + expMonth: string = null; + @linkedFieldOption(LinkedId.ExpYear, "expirationYear") + expYear: string = null; + @linkedFieldOption(LinkedId.Code, "securityCode") + code: string = null; + + private _brand: string = null; + private _number: string = null; + private _subTitle: string = null; + + constructor() { + super(); + } + + get maskedCode(): string { + return this.code != null ? "•".repeat(this.code.length) : null; + } + + get maskedNumber(): string { + return this.number != null ? "•".repeat(this.number.length) : null; + } + + @linkedFieldOption(LinkedId.Brand) + get brand(): string { + return this._brand; + } + set brand(value: string) { + this._brand = value; + this._subTitle = null; + } + + @linkedFieldOption(LinkedId.Number) + get number(): string { + return this._number; + } + set number(value: string) { + this._number = value; + this._subTitle = null; + } + + get subTitle(): string { + if (this._subTitle == null) { + this._subTitle = this.brand; + if (this.number != null && this.number.length >= 4) { + if (this._subTitle != null && this._subTitle !== "") { + this._subTitle += ", "; + } else { + this._subTitle = ""; + } + + // Show last 5 on amex, last 4 for all others + const count = + this.number.length >= 5 && this.number.match(new RegExp("^3[47]")) != null ? 5 : 4; + this._subTitle += "*" + this.number.substr(this.number.length - count); + } + } + return this._subTitle; + } + + get expiration(): string { + if (!this.expMonth && !this.expYear) { + return null; + } + + let exp = this.expMonth != null ? ("0" + this.expMonth).slice(-2) : "__"; + exp += " / " + (this.expYear != null ? this.formatYear(this.expYear) : "____"); + return exp; + } + + private formatYear(year: string): string { + return year.length === 2 ? "20" + year : year; + } +} diff --git a/jslib/common/src/models/view/cipherView.ts b/jslib/common/src/models/view/cipherView.ts new file mode 100644 index 00000000..3d1f16c1 --- /dev/null +++ b/jslib/common/src/models/view/cipherView.ts @@ -0,0 +1,134 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { Cipher } from "../domain/cipher"; + +import { AttachmentView } from "./attachmentView"; +import { CardView } from "./cardView"; +import { FieldView } from "./fieldView"; +import { IdentityView } from "./identityView"; +import { LoginView } from "./loginView"; +import { PasswordHistoryView } from "./passwordHistoryView"; +import { SecureNoteView } from "./secureNoteView"; +import { View } from "./view"; + +export class CipherView implements View { + id: string = null; + organizationId: string = null; + folderId: string = null; + name: string = null; + notes: string = null; + type: CipherType = null; + favorite = false; + organizationUseTotp = false; + edit = false; + viewPassword = true; + localData: any; + login = new LoginView(); + identity = new IdentityView(); + card = new CardView(); + secureNote = new SecureNoteView(); + attachments: AttachmentView[] = null; + fields: FieldView[] = null; + passwordHistory: PasswordHistoryView[] = null; + collectionIds: string[] = null; + revisionDate: Date = null; + deletedDate: Date = null; + reprompt: CipherRepromptType = CipherRepromptType.None; + + constructor(c?: Cipher) { + if (!c) { + return; + } + + this.id = c.id; + this.organizationId = c.organizationId; + this.folderId = c.folderId; + this.favorite = c.favorite; + this.organizationUseTotp = c.organizationUseTotp; + this.edit = c.edit; + this.viewPassword = c.viewPassword; + this.type = c.type; + this.localData = c.localData; + this.collectionIds = c.collectionIds; + this.revisionDate = c.revisionDate; + this.deletedDate = c.deletedDate; + // Old locally stored ciphers might have reprompt == null. If so set it to None. + this.reprompt = c.reprompt ?? CipherRepromptType.None; + } + + private get item() { + switch (this.type) { + case CipherType.Login: + return this.login; + case CipherType.SecureNote: + return this.secureNote; + case CipherType.Card: + return this.card; + case CipherType.Identity: + return this.identity; + default: + break; + } + + return null; + } + + get subTitle(): string { + return this.item.subTitle; + } + + get hasPasswordHistory(): boolean { + return this.passwordHistory && this.passwordHistory.length > 0; + } + + get hasAttachments(): boolean { + return this.attachments && this.attachments.length > 0; + } + + get hasOldAttachments(): boolean { + if (this.hasAttachments) { + for (let i = 0; i < this.attachments.length; i++) { + if (this.attachments[i].key == null) { + return true; + } + } + } + return false; + } + + get hasFields(): boolean { + return this.fields && this.fields.length > 0; + } + + get passwordRevisionDisplayDate(): Date { + if (this.type !== CipherType.Login || this.login == null) { + return null; + } else if (this.login.password == null || this.login.password === "") { + return null; + } + return this.login.passwordRevisionDate; + } + + get isDeleted(): boolean { + return this.deletedDate != null; + } + + get linkedFieldOptions() { + return this.item.linkedFieldOptions; + } + + linkedFieldValue(id: LinkedIdType) { + const linkedFieldOption = this.linkedFieldOptions?.get(id); + if (linkedFieldOption == null) { + return null; + } + + const item = this.item; + return this.item[linkedFieldOption.propertyKey as keyof typeof item]; + } + + linkedFieldI18nKey(id: LinkedIdType): string { + return this.linkedFieldOptions.get(id)?.i18nKey; + } +} diff --git a/jslib/common/src/models/view/collectionView.ts b/jslib/common/src/models/view/collectionView.ts new file mode 100644 index 00000000..d230e591 --- /dev/null +++ b/jslib/common/src/models/view/collectionView.ts @@ -0,0 +1,28 @@ +import { Collection } from "../domain/collection"; +import { ITreeNodeObject } from "../domain/treeNode"; +import { CollectionGroupDetailsResponse } from "../response/collectionResponse"; + +import { View } from "./view"; + +export class CollectionView implements View, ITreeNodeObject { + id: string = null; + organizationId: string = null; + name: string = null; + externalId: string = null; + readOnly: boolean = null; + hidePasswords: boolean = null; + + constructor(c?: Collection | CollectionGroupDetailsResponse) { + if (!c) { + return; + } + + this.id = c.id; + this.organizationId = c.organizationId; + this.externalId = c.externalId; + if (c instanceof Collection) { + this.readOnly = c.readOnly; + this.hidePasswords = c.hidePasswords; + } + } +} diff --git a/jslib/common/src/models/view/eventView.ts b/jslib/common/src/models/view/eventView.ts new file mode 100644 index 00000000..17339ecb --- /dev/null +++ b/jslib/common/src/models/view/eventView.ts @@ -0,0 +1,27 @@ +import { EventType } from "../../enums/eventType"; + +export class EventView { + message: string; + humanReadableMessage: string; + appIcon: string; + appName: string; + userId: string; + userName: string; + userEmail: string; + date: string; + ip: string; + type: EventType; + + constructor(data: Required) { + this.message = data.message; + this.humanReadableMessage = data.humanReadableMessage; + this.appIcon = data.appIcon; + this.appName = data.appName; + this.userId = data.userId; + this.userName = data.userName; + this.userEmail = data.userEmail; + this.date = data.date; + this.ip = data.ip; + this.type = data.type; + } +} diff --git a/jslib/common/src/models/view/fieldView.ts b/jslib/common/src/models/view/fieldView.ts new file mode 100644 index 00000000..51e3aaef --- /dev/null +++ b/jslib/common/src/models/view/fieldView.ts @@ -0,0 +1,28 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { Field } from "../domain/field"; + +import { View } from "./view"; + +export class FieldView implements View { + name: string = null; + value: string = null; + type: FieldType = null; + newField = false; // Marks if the field is new and hasn't been saved + showValue = false; + showCount = false; + linkedId: LinkedIdType = null; + + constructor(f?: Field) { + if (!f) { + return; + } + + this.type = f.type; + this.linkedId = f.linkedId; + } + + get maskedValue(): string { + return this.value != null ? "••••••••" : null; + } +} diff --git a/jslib/common/src/models/view/folderView.ts b/jslib/common/src/models/view/folderView.ts new file mode 100644 index 00000000..731acffb --- /dev/null +++ b/jslib/common/src/models/view/folderView.ts @@ -0,0 +1,19 @@ +import { Folder } from "../domain/folder"; +import { ITreeNodeObject } from "../domain/treeNode"; + +import { View } from "./view"; + +export class FolderView implements View, ITreeNodeObject { + id: string = null; + name: string = null; + revisionDate: Date = null; + + constructor(f?: Folder) { + if (!f) { + return; + } + + this.id = f.id; + this.revisionDate = f.revisionDate; + } +} diff --git a/jslib/common/src/models/view/identityView.ts b/jslib/common/src/models/view/identityView.ts new file mode 100644 index 00000000..26305636 --- /dev/null +++ b/jslib/common/src/models/view/identityView.ts @@ -0,0 +1,142 @@ +import { IdentityLinkedId as LinkedId } from "../../enums/linkedIdType"; +import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator"; +import { Utils } from "../../misc/utils"; + +import { ItemView } from "./itemView"; + +export class IdentityView extends ItemView { + @linkedFieldOption(LinkedId.Title) + title: string = null; + @linkedFieldOption(LinkedId.MiddleName) + middleName: string = null; + @linkedFieldOption(LinkedId.Address1) + address1: string = null; + @linkedFieldOption(LinkedId.Address2) + address2: string = null; + @linkedFieldOption(LinkedId.Address3) + address3: string = null; + @linkedFieldOption(LinkedId.City, "cityTown") + city: string = null; + @linkedFieldOption(LinkedId.State, "stateProvince") + state: string = null; + @linkedFieldOption(LinkedId.PostalCode, "zipPostalCode") + postalCode: string = null; + @linkedFieldOption(LinkedId.Country) + country: string = null; + @linkedFieldOption(LinkedId.Company) + company: string = null; + @linkedFieldOption(LinkedId.Email) + email: string = null; + @linkedFieldOption(LinkedId.Phone) + phone: string = null; + @linkedFieldOption(LinkedId.Ssn) + ssn: string = null; + @linkedFieldOption(LinkedId.Username) + username: string = null; + @linkedFieldOption(LinkedId.PassportNumber) + passportNumber: string = null; + @linkedFieldOption(LinkedId.LicenseNumber) + licenseNumber: string = null; + + private _firstName: string = null; + private _lastName: string = null; + private _subTitle: string = null; + + constructor() { + super(); + } + + @linkedFieldOption(LinkedId.FirstName) + get firstName(): string { + return this._firstName; + } + set firstName(value: string) { + this._firstName = value; + this._subTitle = null; + } + + @linkedFieldOption(LinkedId.LastName) + get lastName(): string { + return this._lastName; + } + set lastName(value: string) { + this._lastName = value; + this._subTitle = null; + } + + get subTitle(): string { + if (this._subTitle == null && (this.firstName != null || this.lastName != null)) { + this._subTitle = ""; + if (this.firstName != null) { + this._subTitle = this.firstName; + } + if (this.lastName != null) { + if (this._subTitle !== "") { + this._subTitle += " "; + } + this._subTitle += this.lastName; + } + } + + return this._subTitle; + } + + @linkedFieldOption(LinkedId.FullName) + get fullName(): string { + if ( + this.title != null || + this.firstName != null || + this.middleName != null || + this.lastName != null + ) { + let name = ""; + if (this.title != null) { + name += this.title + " "; + } + if (this.firstName != null) { + name += this.firstName + " "; + } + if (this.middleName != null) { + name += this.middleName + " "; + } + if (this.lastName != null) { + name += this.lastName; + } + return name.trim(); + } + + return null; + } + + get fullAddress(): string { + let address = this.address1; + if (!Utils.isNullOrWhitespace(this.address2)) { + if (!Utils.isNullOrWhitespace(address)) { + address += ", "; + } + address += this.address2; + } + if (!Utils.isNullOrWhitespace(this.address3)) { + if (!Utils.isNullOrWhitespace(address)) { + address += ", "; + } + address += this.address3; + } + return address; + } + + get fullAddressPart2(): string { + if (this.city == null && this.state == null && this.postalCode == null) { + return null; + } + const city = this.city || "-"; + const state = this.state; + const postalCode = this.postalCode || "-"; + let addressPart2 = city; + if (!Utils.isNullOrWhitespace(state)) { + addressPart2 += ", " + state; + } + addressPart2 += ", " + postalCode; + return addressPart2; + } +} diff --git a/jslib/common/src/models/view/itemView.ts b/jslib/common/src/models/view/itemView.ts new file mode 100644 index 00000000..3a557ffa --- /dev/null +++ b/jslib/common/src/models/view/itemView.ts @@ -0,0 +1,8 @@ +import { LinkedMetadata } from "../../misc/linkedFieldOption.decorator"; + +import { View } from "./view"; + +export abstract class ItemView implements View { + linkedFieldOptions: Map; + abstract get subTitle(): string; +} diff --git a/jslib/common/src/models/view/loginUriView.ts b/jslib/common/src/models/view/loginUriView.ts new file mode 100644 index 00000000..cca9520d --- /dev/null +++ b/jslib/common/src/models/view/loginUriView.ts @@ -0,0 +1,127 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { Utils } from "../../misc/utils"; +import { LoginUri } from "../domain/loginUri"; + +import { View } from "./view"; + +const CanLaunchWhitelist = [ + "https://", + "http://", + "ssh://", + "ftp://", + "sftp://", + "irc://", + "vnc://", + // https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/remote-desktop-uri + "rdp://", // Legacy RDP URI scheme + "ms-rd:", // Preferred RDP URI scheme + "chrome://", + "iosapp://", + "androidapp://", +]; + +export class LoginUriView implements View { + match: UriMatchType = null; + + private _uri: string = null; + private _domain: string = null; + private _hostname: string = null; + private _host: string = null; + private _canLaunch: boolean = null; + + constructor(u?: LoginUri) { + if (!u) { + return; + } + + this.match = u.match; + } + + get uri(): string { + return this._uri; + } + set uri(value: string) { + this._uri = value; + this._domain = null; + this._canLaunch = null; + } + + get domain(): string { + if (this._domain == null && this.uri != null) { + this._domain = Utils.getDomain(this.uri); + if (this._domain === "") { + this._domain = null; + } + } + + return this._domain; + } + + get hostname(): string { + if (this.match === UriMatchType.RegularExpression) { + return null; + } + if (this._hostname == null && this.uri != null) { + this._hostname = Utils.getHostname(this.uri); + if (this._hostname === "") { + this._hostname = null; + } + } + + return this._hostname; + } + + get host(): string { + if (this.match === UriMatchType.RegularExpression) { + return null; + } + if (this._host == null && this.uri != null) { + this._host = Utils.getHost(this.uri); + if (this._host === "") { + this._host = null; + } + } + + return this._host; + } + + get hostnameOrUri(): string { + return this.hostname != null ? this.hostname : this.uri; + } + + get hostOrUri(): string { + return this.host != null ? this.host : this.uri; + } + + get isWebsite(): boolean { + return ( + this.uri != null && + (this.uri.indexOf("http://") === 0 || + this.uri.indexOf("https://") === 0 || + (this.uri.indexOf("://") < 0 && Utils.tldEndingRegex.test(this.uri))) + ); + } + + get canLaunch(): boolean { + if (this._canLaunch != null) { + return this._canLaunch; + } + if (this.uri != null && this.match !== UriMatchType.RegularExpression) { + const uri = this.launchUri; + for (let i = 0; i < CanLaunchWhitelist.length; i++) { + if (uri.indexOf(CanLaunchWhitelist[i]) === 0) { + this._canLaunch = true; + return this._canLaunch; + } + } + } + this._canLaunch = false; + return this._canLaunch; + } + + get launchUri(): string { + return this.uri.indexOf("://") < 0 && Utils.tldEndingRegex.test(this.uri) + ? "http://" + this.uri + : this.uri; + } +} diff --git a/jslib/common/src/models/view/loginView.ts b/jslib/common/src/models/view/loginView.ts new file mode 100644 index 00000000..e86dd14b --- /dev/null +++ b/jslib/common/src/models/view/loginView.ts @@ -0,0 +1,63 @@ +import { LoginLinkedId as LinkedId } from "../../enums/linkedIdType"; +import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator"; +import { Utils } from "../../misc/utils"; +import { Login } from "../domain/login"; + +import { ItemView } from "./itemView"; +import { LoginUriView } from "./loginUriView"; + +export class LoginView extends ItemView { + @linkedFieldOption(LinkedId.Username) + username: string = null; + @linkedFieldOption(LinkedId.Password) + password: string = null; + + passwordRevisionDate?: Date = null; + totp: string = null; + uris: LoginUriView[] = null; + autofillOnPageLoad: boolean = null; + + constructor(l?: Login) { + super(); + if (!l) { + return; + } + + this.passwordRevisionDate = l.passwordRevisionDate; + this.autofillOnPageLoad = l.autofillOnPageLoad; + } + + get uri(): string { + return this.hasUris ? this.uris[0].uri : null; + } + + get maskedPassword(): string { + return this.password != null ? "••••••••" : null; + } + + get subTitle(): string { + return this.username; + } + + get canLaunch(): boolean { + return this.hasUris && this.uris.some((u) => u.canLaunch); + } + + get hasTotp(): boolean { + return !Utils.isNullOrWhitespace(this.totp); + } + + get launchUri(): string { + if (this.hasUris) { + const uri = this.uris.find((u) => u.canLaunch); + if (uri != null) { + return uri.launchUri; + } + } + return null; + } + + get hasUris(): boolean { + return this.uris != null && this.uris.length > 0; + } +} diff --git a/jslib/common/src/models/view/passwordHistoryView.ts b/jslib/common/src/models/view/passwordHistoryView.ts new file mode 100644 index 00000000..aa780079 --- /dev/null +++ b/jslib/common/src/models/view/passwordHistoryView.ts @@ -0,0 +1,16 @@ +import { Password } from "../domain/password"; + +import { View } from "./view"; + +export class PasswordHistoryView implements View { + password: string = null; + lastUsedDate: Date = null; + + constructor(ph?: Password) { + if (!ph) { + return; + } + + this.lastUsedDate = ph.lastUsedDate; + } +} diff --git a/jslib/common/src/models/view/secureNoteView.ts b/jslib/common/src/models/view/secureNoteView.ts new file mode 100644 index 00000000..c324b07e --- /dev/null +++ b/jslib/common/src/models/view/secureNoteView.ts @@ -0,0 +1,21 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { SecureNote } from "../domain/secureNote"; + +import { ItemView } from "./itemView"; + +export class SecureNoteView extends ItemView { + type: SecureNoteType = null; + + constructor(n?: SecureNote) { + super(); + if (!n) { + return; + } + + this.type = n.type; + } + + get subTitle(): string { + return null; + } +} diff --git a/jslib/common/src/models/view/sendAccessView.ts b/jslib/common/src/models/view/sendAccessView.ts new file mode 100644 index 00000000..71e5276a --- /dev/null +++ b/jslib/common/src/models/view/sendAccessView.ts @@ -0,0 +1,27 @@ +import { SendType } from "../../enums/sendType"; +import { SendAccess } from "../domain/sendAccess"; + +import { SendFileView } from "./sendFileView"; +import { SendTextView } from "./sendTextView"; +import { View } from "./view"; + +export class SendAccessView implements View { + id: string = null; + name: string = null; + type: SendType = null; + text = new SendTextView(); + file = new SendFileView(); + expirationDate: Date = null; + creatorIdentifier: string = null; + + constructor(s?: SendAccess) { + if (!s) { + return; + } + + this.id = s.id; + this.type = s.type; + this.expirationDate = s.expirationDate; + this.creatorIdentifier = s.creatorIdentifier; + } +} diff --git a/jslib/common/src/models/view/sendFileView.ts b/jslib/common/src/models/view/sendFileView.ts new file mode 100644 index 00000000..7d37f2de --- /dev/null +++ b/jslib/common/src/models/view/sendFileView.ts @@ -0,0 +1,31 @@ +import { SendFile } from "../domain/sendFile"; + +import { View } from "./view"; + +export class SendFileView implements View { + id: string = null; + size: string = null; + sizeName: string = null; + fileName: string = null; + + constructor(f?: SendFile) { + if (!f) { + return; + } + + this.id = f.id; + this.size = f.size; + this.sizeName = f.sizeName; + } + + get fileSize(): number { + try { + if (this.size != null) { + return parseInt(this.size, null); + } + } catch { + // Invalid file size. + } + return 0; + } +} diff --git a/jslib/common/src/models/view/sendTextView.ts b/jslib/common/src/models/view/sendTextView.ts new file mode 100644 index 00000000..a2c6b571 --- /dev/null +++ b/jslib/common/src/models/view/sendTextView.ts @@ -0,0 +1,20 @@ +import { SendText } from "../domain/sendText"; + +import { View } from "./view"; + +export class SendTextView implements View { + text: string = null; + hidden: boolean; + + constructor(t?: SendText) { + if (!t) { + return; + } + + this.hidden = t.hidden; + } + + get maskedText(): string { + return this.text != null ? "••••••••" : null; + } +} diff --git a/jslib/common/src/models/view/sendView.ts b/jslib/common/src/models/view/sendView.ts new file mode 100644 index 00000000..1a2a214f --- /dev/null +++ b/jslib/common/src/models/view/sendView.ts @@ -0,0 +1,68 @@ +import { SendType } from "../../enums/sendType"; +import { Utils } from "../../misc/utils"; +import { Send } from "../domain/send"; +import { SymmetricCryptoKey } from "../domain/symmetricCryptoKey"; + +import { SendFileView } from "./sendFileView"; +import { SendTextView } from "./sendTextView"; +import { View } from "./view"; + +export class SendView implements View { + id: string = null; + accessId: string = null; + name: string = null; + notes: string = null; + key: ArrayBuffer; + cryptoKey: SymmetricCryptoKey; + type: SendType = null; + text = new SendTextView(); + file = new SendFileView(); + maxAccessCount?: number = null; + accessCount = 0; + revisionDate: Date = null; + deletionDate: Date = null; + expirationDate: Date = null; + password: string = null; + disabled = false; + hideEmail = false; + + constructor(s?: Send) { + if (!s) { + return; + } + + this.id = s.id; + this.accessId = s.accessId; + this.type = s.type; + this.maxAccessCount = s.maxAccessCount; + this.accessCount = s.accessCount; + this.revisionDate = s.revisionDate; + this.deletionDate = s.deletionDate; + this.expirationDate = s.expirationDate; + this.disabled = s.disabled; + this.password = s.password; + this.hideEmail = s.hideEmail; + } + + get urlB64Key(): string { + return Utils.fromBufferToUrlB64(this.key); + } + + get maxAccessCountReached(): boolean { + if (this.maxAccessCount == null) { + return false; + } + return this.accessCount >= this.maxAccessCount; + } + + get expired(): boolean { + if (this.expirationDate == null) { + return false; + } + return this.expirationDate <= new Date(); + } + + get pendingDelete(): boolean { + return this.deletionDate <= new Date(); + } +} diff --git a/jslib/common/src/models/view/ssoConfigView.ts b/jslib/common/src/models/view/ssoConfigView.ts new file mode 100644 index 00000000..b4784c36 --- /dev/null +++ b/jslib/common/src/models/view/ssoConfigView.ts @@ -0,0 +1,104 @@ +import { + OpenIdConnectRedirectBehavior, + Saml2BindingType, + Saml2NameIdFormat, + Saml2SigningBehavior, + SsoType, +} from "../../enums/ssoEnums"; +import { SsoConfigApi } from "../api/ssoConfigApi"; + +import { View } from "./view"; + +export class SsoConfigView extends View { + configType: SsoType; + + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + openId: { + authority: string; + clientId: string; + clientSecret: string; + metadataAddress: string; + redirectBehavior: OpenIdConnectRedirectBehavior; + getClaimsFromUserInfoEndpoint: boolean; + additionalScopes: string; + additionalUserIdClaimTypes: string; + additionalEmailClaimTypes: string; + additionalNameClaimTypes: string; + acrValues: string; + expectedReturnAcrValue: string; + }; + + saml: { + spNameIdFormat: Saml2NameIdFormat; + spOutboundSigningAlgorithm: string; + spSigningBehavior: Saml2SigningBehavior; + spMinIncomingSigningAlgorithm: boolean; + spWantAssertionsSigned: boolean; + spValidateCertificates: boolean; + + idpEntityId: string; + idpBindingType: Saml2BindingType; + idpSingleSignOnServiceUrl: string; + idpSingleLogoutServiceUrl: string; + idpX509PublicCert: string; + idpOutboundSigningAlgorithm: string; + idpAllowUnsolicitedAuthnResponse: boolean; + idpAllowOutboundLogoutRequests: boolean; + idpWantAuthnRequestsSigned: boolean; + }; + + constructor(api: SsoConfigApi) { + super(); + if (api == null) { + return; + } + + this.configType = api.configType; + + this.keyConnectorEnabled = api.keyConnectorEnabled; + this.keyConnectorUrl = api.keyConnectorUrl; + + if (this.configType === SsoType.OpenIdConnect) { + this.openId = { + authority: api.authority, + clientId: api.clientId, + clientSecret: api.clientSecret, + metadataAddress: api.metadataAddress, + redirectBehavior: api.redirectBehavior, + getClaimsFromUserInfoEndpoint: api.getClaimsFromUserInfoEndpoint, + additionalScopes: api.additionalScopes, + additionalUserIdClaimTypes: api.additionalUserIdClaimTypes, + additionalEmailClaimTypes: api.additionalEmailClaimTypes, + additionalNameClaimTypes: api.additionalNameClaimTypes, + acrValues: api.acrValues, + expectedReturnAcrValue: api.expectedReturnAcrValue, + }; + } else if (this.configType === SsoType.Saml2) { + this.saml = { + spNameIdFormat: api.spNameIdFormat, + spOutboundSigningAlgorithm: api.spOutboundSigningAlgorithm, + spSigningBehavior: api.spSigningBehavior, + spMinIncomingSigningAlgorithm: api.spMinIncomingSigningAlgorithm, + spWantAssertionsSigned: api.spWantAssertionsSigned, + spValidateCertificates: api.spValidateCertificates, + + idpEntityId: api.idpEntityId, + idpBindingType: api.idpBindingType, + idpSingleSignOnServiceUrl: api.idpSingleSignOnServiceUrl, + idpSingleLogoutServiceUrl: api.idpSingleLogoutServiceUrl, + idpX509PublicCert: api.idpX509PublicCert, + idpOutboundSigningAlgorithm: api.idpOutboundSigningAlgorithm, + idpAllowUnsolicitedAuthnResponse: api.idpAllowUnsolicitedAuthnResponse, + idpWantAuthnRequestsSigned: api.idpWantAuthnRequestsSigned, + + // Value is inverted in the view model (allow instead of disable) + idpAllowOutboundLogoutRequests: + api.idpDisableOutboundLogoutRequests == null + ? null + : !api.idpDisableOutboundLogoutRequests, + }; + } + } +} diff --git a/jslib/common/src/models/view/view.ts b/jslib/common/src/models/view/view.ts new file mode 100644 index 00000000..1f16b3d5 --- /dev/null +++ b/jslib/common/src/models/view/view.ts @@ -0,0 +1 @@ +export class View {} diff --git a/jslib/common/src/services/api.service.ts b/jslib/common/src/services/api.service.ts new file mode 100644 index 00000000..da2fcd5d --- /dev/null +++ b/jslib/common/src/services/api.service.ts @@ -0,0 +1,2534 @@ +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { DeviceRequest } from "jslib-common/models/request/deviceRequest"; +import { TokenRequestTwoFactor } from "jslib-common/models/request/identityToken/tokenRequestTwoFactor"; + +import { ApiService as ApiServiceAbstraction } from "../abstractions/api.service"; +import { EnvironmentService } from "../abstractions/environment.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { TokenService } from "../abstractions/token.service"; +import { DeviceType } from "../enums/deviceType"; +import { PolicyType } from "../enums/policyType"; +import { Utils } from "../misc/utils"; +import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest"; +import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest"; +import { AttachmentRequest } from "../models/request/attachmentRequest"; +import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest"; +import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest"; +import { CipherBulkMoveRequest } from "../models/request/cipherBulkMoveRequest"; +import { CipherBulkShareRequest } from "../models/request/cipherBulkShareRequest"; +import { CipherCollectionsRequest } from "../models/request/cipherCollectionsRequest"; +import { CipherCreateRequest } from "../models/request/cipherCreateRequest"; +import { CipherRequest } from "../models/request/cipherRequest"; +import { CipherShareRequest } from "../models/request/cipherShareRequest"; +import { CollectionRequest } from "../models/request/collectionRequest"; +import { DeleteRecoverRequest } from "../models/request/deleteRecoverRequest"; +import { EmailRequest } from "../models/request/emailRequest"; +import { EmailTokenRequest } from "../models/request/emailTokenRequest"; +import { EmergencyAccessAcceptRequest } from "../models/request/emergencyAccessAcceptRequest"; +import { EmergencyAccessConfirmRequest } from "../models/request/emergencyAccessConfirmRequest"; +import { EmergencyAccessInviteRequest } from "../models/request/emergencyAccessInviteRequest"; +import { EmergencyAccessPasswordRequest } from "../models/request/emergencyAccessPasswordRequest"; +import { EmergencyAccessUpdateRequest } from "../models/request/emergencyAccessUpdateRequest"; +import { EventRequest } from "../models/request/eventRequest"; +import { FolderRequest } from "../models/request/folderRequest"; +import { GroupRequest } from "../models/request/groupRequest"; +import { IapCheckRequest } from "../models/request/iapCheckRequest"; +import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest"; +import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest"; +import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest"; +import { ImportCiphersRequest } from "../models/request/importCiphersRequest"; +import { ImportDirectoryRequest } from "../models/request/importDirectoryRequest"; +import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest"; +import { KdfRequest } from "../models/request/kdfRequest"; +import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest"; +import { KeysRequest } from "../models/request/keysRequest"; +import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organizationSponsorshipCreateRequest"; +import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organizationSponsorshipRedeemRequest"; +import { OrganizationSsoRequest } from "../models/request/organization/organizationSsoRequest"; +import { OrganizationCreateRequest } from "../models/request/organizationCreateRequest"; +import { OrganizationImportRequest } from "../models/request/organizationImportRequest"; +import { OrganizationKeysRequest } from "../models/request/organizationKeysRequest"; +import { OrganizationSubscriptionUpdateRequest } from "../models/request/organizationSubscriptionUpdateRequest"; +import { OrganizationTaxInfoUpdateRequest } from "../models/request/organizationTaxInfoUpdateRequest"; +import { OrganizationUpdateRequest } from "../models/request/organizationUpdateRequest"; +import { OrganizationUpgradeRequest } from "../models/request/organizationUpgradeRequest"; +import { OrganizationUserAcceptRequest } from "../models/request/organizationUserAcceptRequest"; +import { OrganizationUserBulkConfirmRequest } from "../models/request/organizationUserBulkConfirmRequest"; +import { OrganizationUserBulkRequest } from "../models/request/organizationUserBulkRequest"; +import { OrganizationUserConfirmRequest } from "../models/request/organizationUserConfirmRequest"; +import { OrganizationUserInviteRequest } from "../models/request/organizationUserInviteRequest"; +import { OrganizationUserResetPasswordEnrollmentRequest } from "../models/request/organizationUserResetPasswordEnrollmentRequest"; +import { OrganizationUserResetPasswordRequest } from "../models/request/organizationUserResetPasswordRequest"; +import { OrganizationUserUpdateGroupsRequest } from "../models/request/organizationUserUpdateGroupsRequest"; +import { OrganizationUserUpdateRequest } from "../models/request/organizationUserUpdateRequest"; +import { PasswordHintRequest } from "../models/request/passwordHintRequest"; +import { PasswordRequest } from "../models/request/passwordRequest"; +import { PaymentRequest } from "../models/request/paymentRequest"; +import { PolicyRequest } from "../models/request/policyRequest"; +import { PreloginRequest } from "../models/request/preloginRequest"; +import { ProviderAddOrganizationRequest } from "../models/request/provider/providerAddOrganizationRequest"; +import { ProviderOrganizationCreateRequest } from "../models/request/provider/providerOrganizationCreateRequest"; +import { ProviderSetupRequest } from "../models/request/provider/providerSetupRequest"; +import { ProviderUpdateRequest } from "../models/request/provider/providerUpdateRequest"; +import { ProviderUserAcceptRequest } from "../models/request/provider/providerUserAcceptRequest"; +import { ProviderUserBulkConfirmRequest } from "../models/request/provider/providerUserBulkConfirmRequest"; +import { ProviderUserBulkRequest } from "../models/request/provider/providerUserBulkRequest"; +import { ProviderUserConfirmRequest } from "../models/request/provider/providerUserConfirmRequest"; +import { ProviderUserInviteRequest } from "../models/request/provider/providerUserInviteRequest"; +import { ProviderUserUpdateRequest } from "../models/request/provider/providerUserUpdateRequest"; +import { RegisterRequest } from "../models/request/registerRequest"; +import { SeatRequest } from "../models/request/seatRequest"; +import { SecretVerificationRequest } from "../models/request/secretVerificationRequest"; +import { SelectionReadOnlyRequest } from "../models/request/selectionReadOnlyRequest"; +import { SendAccessRequest } from "../models/request/sendAccessRequest"; +import { SendRequest } from "../models/request/sendRequest"; +import { SetPasswordRequest } from "../models/request/setPasswordRequest"; +import { StorageRequest } from "../models/request/storageRequest"; +import { TaxInfoUpdateRequest } from "../models/request/taxInfoUpdateRequest"; +import { TwoFactorEmailRequest } from "../models/request/twoFactorEmailRequest"; +import { TwoFactorProviderRequest } from "../models/request/twoFactorProviderRequest"; +import { TwoFactorRecoveryRequest } from "../models/request/twoFactorRecoveryRequest"; +import { UpdateDomainsRequest } from "../models/request/updateDomainsRequest"; +import { UpdateKeyRequest } from "../models/request/updateKeyRequest"; +import { UpdateProfileRequest } from "../models/request/updateProfileRequest"; +import { UpdateTempPasswordRequest } from "../models/request/updateTempPasswordRequest"; +import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/updateTwoFactorAuthenticatorRequest"; +import { UpdateTwoFactorDuoRequest } from "../models/request/updateTwoFactorDuoRequest"; +import { UpdateTwoFactorEmailRequest } from "../models/request/updateTwoFactorEmailRequest"; +import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/updateTwoFactorWebAuthnDeleteRequest"; +import { UpdateTwoFactorWebAuthnRequest } from "../models/request/updateTwoFactorWebAuthnRequest"; +import { UpdateTwoFactorYubioOtpRequest } from "../models/request/updateTwoFactorYubioOtpRequest"; +import { VerifyBankRequest } from "../models/request/verifyBankRequest"; +import { VerifyDeleteRecoverRequest } from "../models/request/verifyDeleteRecoverRequest"; +import { VerifyEmailRequest } from "../models/request/verifyEmailRequest"; +import { ApiKeyResponse } from "../models/response/apiKeyResponse"; +import { AttachmentResponse } from "../models/response/attachmentResponse"; +import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { BillingResponse } from "../models/response/billingResponse"; +import { BreachAccountResponse } from "../models/response/breachAccountResponse"; +import { CipherResponse } from "../models/response/cipherResponse"; +import { + CollectionGroupDetailsResponse, + CollectionResponse, +} from "../models/response/collectionResponse"; +import { DomainsResponse } from "../models/response/domainsResponse"; +import { + EmergencyAccessGranteeDetailsResponse, + EmergencyAccessGrantorDetailsResponse, + EmergencyAccessTakeoverResponse, + EmergencyAccessViewResponse, +} from "../models/response/emergencyAccessResponse"; +import { ErrorResponse } from "../models/response/errorResponse"; +import { EventResponse } from "../models/response/eventResponse"; +import { FolderResponse } from "../models/response/folderResponse"; +import { GroupDetailsResponse, GroupResponse } from "../models/response/groupResponse"; +import { IdentityCaptchaResponse } from "../models/response/identityCaptchaResponse"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; +import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; +import { KeyConnectorUserKeyResponse } from "../models/response/keyConnectorUserKeyResponse"; +import { ListResponse } from "../models/response/listResponse"; +import { OrganizationSsoResponse } from "../models/response/organization/organizationSsoResponse"; +import { OrganizationAutoEnrollStatusResponse } from "../models/response/organizationAutoEnrollStatusResponse"; +import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse"; +import { OrganizationResponse } from "../models/response/organizationResponse"; +import { OrganizationSubscriptionResponse } from "../models/response/organizationSubscriptionResponse"; +import { OrganizationUserBulkPublicKeyResponse } from "../models/response/organizationUserBulkPublicKeyResponse"; +import { OrganizationUserBulkResponse } from "../models/response/organizationUserBulkResponse"; +import { + OrganizationUserDetailsResponse, + OrganizationUserResetPasswordDetailsReponse, + OrganizationUserUserDetailsResponse, +} from "../models/response/organizationUserResponse"; +import { PaymentResponse } from "../models/response/paymentResponse"; +import { PlanResponse } from "../models/response/planResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; +import { PreloginResponse } from "../models/response/preloginResponse"; +import { ProfileResponse } from "../models/response/profileResponse"; +import { + ProviderOrganizationOrganizationDetailsResponse, + ProviderOrganizationResponse, +} from "../models/response/provider/providerOrganizationResponse"; +import { ProviderResponse } from "../models/response/provider/providerResponse"; +import { ProviderUserBulkPublicKeyResponse } from "../models/response/provider/providerUserBulkPublicKeyResponse"; +import { ProviderUserBulkResponse } from "../models/response/provider/providerUserBulkResponse"; +import { + ProviderUserResponse, + ProviderUserUserDetailsResponse, +} from "../models/response/provider/providerUserResponse"; +import { SelectionReadOnlyResponse } from "../models/response/selectionReadOnlyResponse"; +import { SendAccessResponse } from "../models/response/sendAccessResponse"; +import { SendFileDownloadDataResponse } from "../models/response/sendFileDownloadDataResponse"; +import { SendFileUploadDataResponse } from "../models/response/sendFileUploadDataResponse"; +import { SendResponse } from "../models/response/sendResponse"; +import { SubscriptionResponse } from "../models/response/subscriptionResponse"; +import { SyncResponse } from "../models/response/syncResponse"; +import { TaxInfoResponse } from "../models/response/taxInfoResponse"; +import { TaxRateResponse } from "../models/response/taxRateResponse"; +import { TwoFactorAuthenticatorResponse } from "../models/response/twoFactorAuthenticatorResponse"; +import { TwoFactorDuoResponse } from "../models/response/twoFactorDuoResponse"; +import { TwoFactorEmailResponse } from "../models/response/twoFactorEmailResponse"; +import { TwoFactorProviderResponse } from "../models/response/twoFactorProviderResponse"; +import { TwoFactorRecoverResponse } from "../models/response/twoFactorRescoverResponse"; +import { + TwoFactorWebAuthnResponse, + ChallengeResponse, +} from "../models/response/twoFactorWebAuthnResponse"; +import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyResponse"; +import { UserKeyResponse } from "../models/response/userKeyResponse"; +import { SendAccessView } from "../models/view/sendAccessView"; + +export class ApiService implements ApiServiceAbstraction { + private device: DeviceType; + private deviceType: string; + private isWebClient = false; + private isDesktopClient = false; + + constructor( + private tokenService: TokenService, + private platformUtilsService: PlatformUtilsService, + private environmentService: EnvironmentService, + private appIdService: AppIdService, + private logoutCallback: (expired: boolean) => Promise, + private customUserAgent: string = null + ) { + this.device = platformUtilsService.getDevice(); + this.deviceType = this.device.toString(); + this.isWebClient = + this.device === DeviceType.IEBrowser || + this.device === DeviceType.ChromeBrowser || + this.device === DeviceType.EdgeBrowser || + this.device === DeviceType.FirefoxBrowser || + this.device === DeviceType.OperaBrowser || + this.device === DeviceType.SafariBrowser || + this.device === DeviceType.UnknownBrowser || + this.device === DeviceType.VivaldiBrowser; + this.isDesktopClient = + this.device === DeviceType.WindowsDesktop || + this.device === DeviceType.MacOsDesktop || + this.device === DeviceType.LinuxDesktop; + } + + // Auth APIs + + async postIdentityToken( + request: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest + ): Promise { + const headers = new Headers({ + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + Accept: "application/json", + "Device-Type": this.deviceType, + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + request.alterIdentityTokenHeaders(headers); + + const identityToken = + request instanceof ApiTokenRequest + ? request.toIdentityToken() + : request.toIdentityToken(this.platformUtilsService.getClientType()); + + const response = await this.fetch( + new Request(this.environmentService.getIdentityUrl() + "/connect/token", { + body: this.qsStringify(identityToken), + credentials: this.getCredentials(), + cache: "no-store", + headers: headers, + method: "POST", + }) + ); + + let responseJson: any = null; + if (this.isJsonResponse(response)) { + responseJson = await response.json(); + } + + if (responseJson != null) { + if (response.status === 200) { + return new IdentityTokenResponse(responseJson); + } else if ( + response.status === 400 && + responseJson.TwoFactorProviders2 && + Object.keys(responseJson.TwoFactorProviders2).length + ) { + await this.tokenService.clearTwoFactorToken(); + return new IdentityTwoFactorResponse(responseJson); + } else if ( + response.status === 400 && + responseJson.HCaptcha_SiteKey && + Object.keys(responseJson.HCaptcha_SiteKey).length + ) { + return new IdentityCaptchaResponse(responseJson); + } + } + + return Promise.reject(new ErrorResponse(responseJson, response.status, true)); + } + + async refreshIdentityToken(): Promise { + try { + await this.doAuthRefresh(); + } catch (e) { + return Promise.reject(null); + } + } + + // Account APIs + + async getProfile(): Promise { + const r = await this.send("GET", "/accounts/profile", null, true, true); + return new ProfileResponse(r); + } + + async getUserBilling(): Promise { + const r = await this.send("GET", "/accounts/billing", null, true, true); + return new BillingResponse(r); + } + + async getUserSubscription(): Promise { + const r = await this.send("GET", "/accounts/subscription", null, true, true); + return new SubscriptionResponse(r); + } + + async getTaxInfo(): Promise { + const r = await this.send("GET", "/accounts/tax", null, true, true); + return new TaxInfoResponse(r); + } + + async putProfile(request: UpdateProfileRequest): Promise { + const r = await this.send("PUT", "/accounts/profile", request, true, true); + return new ProfileResponse(r); + } + + putTaxInfo(request: TaxInfoUpdateRequest): Promise { + return this.send("PUT", "/accounts/tax", request, true, false); + } + + async postPrelogin(request: PreloginRequest): Promise { + const r = await this.send( + "POST", + "/accounts/prelogin", + request, + false, + true, + this.platformUtilsService.isDev() + ? this.environmentService.getIdentityUrl() + : this.environmentService.getApiUrl() + ); + return new PreloginResponse(r); + } + + postEmailToken(request: EmailTokenRequest): Promise { + return this.send("POST", "/accounts/email-token", request, true, false); + } + + postEmail(request: EmailRequest): Promise { + return this.send("POST", "/accounts/email", request, true, false); + } + + postPassword(request: PasswordRequest): Promise { + return this.send("POST", "/accounts/password", request, true, false); + } + + setPassword(request: SetPasswordRequest): Promise { + return this.send("POST", "/accounts/set-password", request, true, false); + } + + postSetKeyConnectorKey(request: SetKeyConnectorKeyRequest): Promise { + return this.send("POST", "/accounts/set-key-connector-key", request, true, false); + } + + postSecurityStamp(request: SecretVerificationRequest): Promise { + return this.send("POST", "/accounts/security-stamp", request, true, false); + } + + deleteAccount(request: SecretVerificationRequest): Promise { + return this.send("DELETE", "/accounts", request, true, false); + } + + async getAccountRevisionDate(): Promise { + const r = await this.send("GET", "/accounts/revision-date", null, true, true); + return r as number; + } + + postPasswordHint(request: PasswordHintRequest): Promise { + return this.send("POST", "/accounts/password-hint", request, false, false); + } + + postRegister(request: RegisterRequest): Promise { + return this.send( + "POST", + "/accounts/register", + request, + false, + false, + this.platformUtilsService.isDev() + ? this.environmentService.getIdentityUrl() + : this.environmentService.getApiUrl() + ); + } + + async postPremium(data: FormData): Promise { + const r = await this.send("POST", "/accounts/premium", data, true, true); + return new PaymentResponse(r); + } + + async postIapCheck(request: IapCheckRequest): Promise { + return this.send("POST", "/accounts/iap-check", request, true, false); + } + + postReinstatePremium(): Promise { + return this.send("POST", "/accounts/reinstate-premium", null, true, false); + } + + postCancelPremium(): Promise { + return this.send("POST", "/accounts/cancel-premium", null, true, false); + } + + async postAccountStorage(request: StorageRequest): Promise { + const r = await this.send("POST", "/accounts/storage", request, true, true); + return new PaymentResponse(r); + } + + postAccountPayment(request: PaymentRequest): Promise { + return this.send("POST", "/accounts/payment", request, true, false); + } + + postAccountLicense(data: FormData): Promise { + return this.send("POST", "/accounts/license", data, true, false); + } + + postAccountKeys(request: KeysRequest): Promise { + return this.send("POST", "/accounts/keys", request, true, false); + } + + postAccountKey(request: UpdateKeyRequest): Promise { + return this.send("POST", "/accounts/key", request, true, false); + } + + postAccountVerifyEmail(): Promise { + return this.send("POST", "/accounts/verify-email", null, true, false); + } + + postAccountVerifyEmailToken(request: VerifyEmailRequest): Promise { + return this.send("POST", "/accounts/verify-email-token", request, false, false); + } + + postAccountVerifyPassword(request: SecretVerificationRequest): Promise { + return this.send("POST", "/accounts/verify-password", request, true, false); + } + + postAccountRecoverDelete(request: DeleteRecoverRequest): Promise { + return this.send("POST", "/accounts/delete-recover", request, false, false); + } + + postAccountRecoverDeleteToken(request: VerifyDeleteRecoverRequest): Promise { + return this.send("POST", "/accounts/delete-recover-token", request, false, false); + } + + postAccountKdf(request: KdfRequest): Promise { + return this.send("POST", "/accounts/kdf", request, true, false); + } + + async deleteSsoUser(organizationId: string): Promise { + return this.send("DELETE", "/accounts/sso/" + organizationId, null, true, false); + } + + async getSsoUserIdentifier(): Promise { + return this.send("GET", "/accounts/sso/user-identifier", null, true, true); + } + + async postUserApiKey(id: string, request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/accounts/api-key", request, true, true); + return new ApiKeyResponse(r); + } + + async postUserRotateApiKey( + id: string, + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/accounts/rotate-api-key", request, true, true); + return new ApiKeyResponse(r); + } + + putUpdateTempPassword(request: UpdateTempPasswordRequest): Promise { + return this.send("PUT", "/accounts/update-temp-password", request, true, false); + } + + postAccountRequestOTP(): Promise { + return this.send("POST", "/accounts/request-otp", null, true, false); + } + + postAccountVerifyOTP(request: VerifyOTPRequest): Promise { + return this.send("POST", "/accounts/verify-otp", request, true, false); + } + + postConvertToKeyConnector(): Promise { + return this.send("POST", "/accounts/convert-to-key-connector", null, true, false); + } + + // Folder APIs + + async getFolder(id: string): Promise { + const r = await this.send("GET", "/folders/" + id, null, true, true); + return new FolderResponse(r); + } + + async postFolder(request: FolderRequest): Promise { + const r = await this.send("POST", "/folders", request, true, true); + return new FolderResponse(r); + } + + async putFolder(id: string, request: FolderRequest): Promise { + const r = await this.send("PUT", "/folders/" + id, request, true, true); + return new FolderResponse(r); + } + + deleteFolder(id: string): Promise { + return this.send("DELETE", "/folders/" + id, null, true, false); + } + + // Send APIs + + async getSend(id: string): Promise { + const r = await this.send("GET", "/sends/" + id, null, true, true); + return new SendResponse(r); + } + + async postSendAccess( + id: string, + request: SendAccessRequest, + apiUrl?: string + ): Promise { + const addSendIdHeader = (headers: Headers) => { + headers.set("Send-Id", id); + }; + const r = await this.send( + "POST", + "/sends/access/" + id, + request, + false, + true, + apiUrl, + addSendIdHeader + ); + return new SendAccessResponse(r); + } + + async getSendFileDownloadData( + send: SendAccessView, + request: SendAccessRequest, + apiUrl?: string + ): Promise { + const addSendIdHeader = (headers: Headers) => { + headers.set("Send-Id", send.id); + }; + const r = await this.send( + "POST", + "/sends/" + send.id + "/access/file/" + send.file.id, + request, + false, + true, + apiUrl, + addSendIdHeader + ); + return new SendFileDownloadDataResponse(r); + } + + async getSends(): Promise> { + const r = await this.send("GET", "/sends", null, true, true); + return new ListResponse(r, SendResponse); + } + + async postSend(request: SendRequest): Promise { + const r = await this.send("POST", "/sends", request, true, true); + return new SendResponse(r); + } + + async postFileTypeSend(request: SendRequest): Promise { + const r = await this.send("POST", "/sends/file/v2", request, true, true); + return new SendFileUploadDataResponse(r); + } + + async renewSendFileUploadUrl( + sendId: string, + fileId: string + ): Promise { + const r = await this.send("GET", "/sends/" + sendId + "/file/" + fileId, null, true, true); + return new SendFileUploadDataResponse(r); + } + + postSendFile(sendId: string, fileId: string, data: FormData): Promise { + return this.send("POST", "/sends/" + sendId + "/file/" + fileId, data, true, false); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async postSendFileLegacy(data: FormData): Promise { + const r = await this.send("POST", "/sends/file", data, true, true); + return new SendResponse(r); + } + + async putSend(id: string, request: SendRequest): Promise { + const r = await this.send("PUT", "/sends/" + id, request, true, true); + return new SendResponse(r); + } + + async putSendRemovePassword(id: string): Promise { + const r = await this.send("PUT", "/sends/" + id + "/remove-password", null, true, true); + return new SendResponse(r); + } + + deleteSend(id: string): Promise { + return this.send("DELETE", "/sends/" + id, null, true, false); + } + + // Cipher APIs + + async getCipher(id: string): Promise { + const r = await this.send("GET", "/ciphers/" + id, null, true, true); + return new CipherResponse(r); + } + + async getCipherAdmin(id: string): Promise { + const r = await this.send("GET", "/ciphers/" + id + "/admin", null, true, true); + return new CipherResponse(r); + } + + async getCiphersOrganization(organizationId: string): Promise> { + const r = await this.send( + "GET", + "/ciphers/organization-details?organizationId=" + organizationId, + null, + true, + true + ); + return new ListResponse(r, CipherResponse); + } + + async postCipher(request: CipherRequest): Promise { + const r = await this.send("POST", "/ciphers", request, true, true); + return new CipherResponse(r); + } + + async postCipherCreate(request: CipherCreateRequest): Promise { + const r = await this.send("POST", "/ciphers/create", request, true, true); + return new CipherResponse(r); + } + + async postCipherAdmin(request: CipherCreateRequest): Promise { + const r = await this.send("POST", "/ciphers/admin", request, true, true); + return new CipherResponse(r); + } + + async putCipher(id: string, request: CipherRequest): Promise { + const r = await this.send("PUT", "/ciphers/" + id, request, true, true); + return new CipherResponse(r); + } + + async putCipherAdmin(id: string, request: CipherRequest): Promise { + const r = await this.send("PUT", "/ciphers/" + id + "/admin", request, true, true); + return new CipherResponse(r); + } + + deleteCipher(id: string): Promise { + return this.send("DELETE", "/ciphers/" + id, null, true, false); + } + + deleteCipherAdmin(id: string): Promise { + return this.send("DELETE", "/ciphers/" + id + "/admin", null, true, false); + } + + deleteManyCiphers(request: CipherBulkDeleteRequest): Promise { + return this.send("DELETE", "/ciphers", request, true, false); + } + + deleteManyCiphersAdmin(request: CipherBulkDeleteRequest): Promise { + return this.send("DELETE", "/ciphers/admin", request, true, false); + } + + putMoveCiphers(request: CipherBulkMoveRequest): Promise { + return this.send("PUT", "/ciphers/move", request, true, false); + } + + async putShareCipher(id: string, request: CipherShareRequest): Promise { + const r = await this.send("PUT", "/ciphers/" + id + "/share", request, true, true); + return new CipherResponse(r); + } + + putShareCiphers(request: CipherBulkShareRequest): Promise { + return this.send("PUT", "/ciphers/share", request, true, false); + } + + putCipherCollections(id: string, request: CipherCollectionsRequest): Promise { + return this.send("PUT", "/ciphers/" + id + "/collections", request, true, false); + } + + putCipherCollectionsAdmin(id: string, request: CipherCollectionsRequest): Promise { + return this.send("PUT", "/ciphers/" + id + "/collections-admin", request, true, false); + } + + postPurgeCiphers( + request: SecretVerificationRequest, + organizationId: string = null + ): Promise { + let path = "/ciphers/purge"; + if (organizationId != null) { + path += "?organizationId=" + organizationId; + } + return this.send("POST", path, request, true, false); + } + + postImportCiphers(request: ImportCiphersRequest): Promise { + return this.send("POST", "/ciphers/import", request, true, false); + } + + postImportOrganizationCiphers( + organizationId: string, + request: ImportOrganizationCiphersRequest + ): Promise { + return this.send( + "POST", + "/ciphers/import-organization?organizationId=" + organizationId, + request, + true, + false + ); + } + + putDeleteCipher(id: string): Promise { + return this.send("PUT", "/ciphers/" + id + "/delete", null, true, false); + } + + putDeleteCipherAdmin(id: string): Promise { + return this.send("PUT", "/ciphers/" + id + "/delete-admin", null, true, false); + } + + putDeleteManyCiphers(request: CipherBulkDeleteRequest): Promise { + return this.send("PUT", "/ciphers/delete", request, true, false); + } + + putDeleteManyCiphersAdmin(request: CipherBulkDeleteRequest): Promise { + return this.send("PUT", "/ciphers/delete-admin", request, true, false); + } + + async putRestoreCipher(id: string): Promise { + const r = await this.send("PUT", "/ciphers/" + id + "/restore", null, true, true); + return new CipherResponse(r); + } + + async putRestoreCipherAdmin(id: string): Promise { + const r = await this.send("PUT", "/ciphers/" + id + "/restore-admin", null, true, true); + return new CipherResponse(r); + } + + async putRestoreManyCiphers( + request: CipherBulkDeleteRequest + ): Promise> { + const r = await this.send("PUT", "/ciphers/restore", request, true, true); + return new ListResponse(r, CipherResponse); + } + + // Attachments APIs + + async getAttachmentData( + cipherId: string, + attachmentId: string, + emergencyAccessId?: string + ): Promise { + const path = + (emergencyAccessId != null ? "/emergency-access/" + emergencyAccessId + "/" : "/ciphers/") + + cipherId + + "/attachment/" + + attachmentId; + const r = await this.send("GET", path, null, true, true); + return new AttachmentResponse(r); + } + + async postCipherAttachment( + id: string, + request: AttachmentRequest + ): Promise { + const r = await this.send("POST", "/ciphers/" + id + "/attachment/v2", request, true, true); + return new AttachmentUploadDataResponse(r); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async postCipherAttachmentLegacy(id: string, data: FormData): Promise { + const r = await this.send("POST", "/ciphers/" + id + "/attachment", data, true, true); + return new CipherResponse(r); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async postCipherAttachmentAdminLegacy(id: string, data: FormData): Promise { + const r = await this.send("POST", "/ciphers/" + id + "/attachment-admin", data, true, true); + return new CipherResponse(r); + } + + deleteCipherAttachment(id: string, attachmentId: string): Promise { + return this.send("DELETE", "/ciphers/" + id + "/attachment/" + attachmentId, null, true, false); + } + + deleteCipherAttachmentAdmin(id: string, attachmentId: string): Promise { + return this.send( + "DELETE", + "/ciphers/" + id + "/attachment/" + attachmentId + "/admin", + null, + true, + false + ); + } + + postShareCipherAttachment( + id: string, + attachmentId: string, + data: FormData, + organizationId: string + ): Promise { + return this.send( + "POST", + "/ciphers/" + id + "/attachment/" + attachmentId + "/share?organizationId=" + organizationId, + data, + true, + false + ); + } + + async renewAttachmentUploadUrl( + id: string, + attachmentId: string + ): Promise { + const r = await this.send( + "GET", + "/ciphers/" + id + "/attachment/" + attachmentId + "/renew", + null, + true, + true + ); + return new AttachmentUploadDataResponse(r); + } + + postAttachmentFile(id: string, attachmentId: string, data: FormData): Promise { + return this.send("POST", "/ciphers/" + id + "/attachment/" + attachmentId, data, true, false); + } + + // Collections APIs + + async getCollectionDetails( + organizationId: string, + id: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/collections/" + id + "/details", + null, + true, + true + ); + return new CollectionGroupDetailsResponse(r); + } + + async getUserCollections(): Promise> { + const r = await this.send("GET", "/collections", null, true, true); + return new ListResponse(r, CollectionResponse); + } + + async getCollections(organizationId: string): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/collections", + null, + true, + true + ); + return new ListResponse(r, CollectionResponse); + } + + async getCollectionUsers( + organizationId: string, + id: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/collections/" + id + "/users", + null, + true, + true + ); + return r.map((dr: any) => new SelectionReadOnlyResponse(dr)); + } + + async postCollection( + organizationId: string, + request: CollectionRequest + ): Promise { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/collections", + request, + true, + true + ); + return new CollectionResponse(r); + } + + async putCollection( + organizationId: string, + id: string, + request: CollectionRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/collections/" + id, + request, + true, + true + ); + return new CollectionResponse(r); + } + + async putCollectionUsers( + organizationId: string, + id: string, + request: SelectionReadOnlyRequest[] + ): Promise { + await this.send( + "PUT", + "/organizations/" + organizationId + "/collections/" + id + "/users", + request, + true, + false + ); + } + + deleteCollection(organizationId: string, id: string): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/collections/" + id, + null, + true, + false + ); + } + + deleteCollectionUser( + organizationId: string, + id: string, + organizationUserId: string + ): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/collections/" + id + "/user/" + organizationUserId, + null, + true, + false + ); + } + + // Groups APIs + + async getGroupDetails(organizationId: string, id: string): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/groups/" + id + "/details", + null, + true, + true + ); + return new GroupDetailsResponse(r); + } + + async getGroups(organizationId: string): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/groups", + null, + true, + true + ); + return new ListResponse(r, GroupResponse); + } + + async getGroupUsers(organizationId: string, id: string): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/groups/" + id + "/users", + null, + true, + true + ); + return r; + } + + async postGroup(organizationId: string, request: GroupRequest): Promise { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/groups", + request, + true, + true + ); + return new GroupResponse(r); + } + + async putGroup( + organizationId: string, + id: string, + request: GroupRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/groups/" + id, + request, + true, + true + ); + return new GroupResponse(r); + } + + async putGroupUsers(organizationId: string, id: string, request: string[]): Promise { + await this.send( + "PUT", + "/organizations/" + organizationId + "/groups/" + id + "/users", + request, + true, + false + ); + } + + deleteGroup(organizationId: string, id: string): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/groups/" + id, + null, + true, + false + ); + } + + deleteGroupUser(organizationId: string, id: string, organizationUserId: string): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/groups/" + id + "/user/" + organizationUserId, + null, + true, + false + ); + } + + // Policy APIs + + async getPolicy(organizationId: string, type: PolicyType): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/policies/" + type, + null, + true, + true + ); + return new PolicyResponse(r); + } + + async getPolicies(organizationId: string): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/policies", + null, + true, + true + ); + return new ListResponse(r, PolicyResponse); + } + + async getPoliciesByToken( + organizationId: string, + token: string, + email: string, + organizationUserId: string + ): Promise> { + const r = await this.send( + "GET", + "/organizations/" + + organizationId + + "/policies/token?" + + "token=" + + encodeURIComponent(token) + + "&email=" + + encodeURIComponent(email) + + "&organizationUserId=" + + organizationUserId, + null, + false, + true + ); + return new ListResponse(r, PolicyResponse); + } + + async getPoliciesByInvitedUser( + organizationId: string, + userId: string + ): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/policies/invited-user?" + "userId=" + userId, + null, + false, + true + ); + return new ListResponse(r, PolicyResponse); + } + + async putPolicy( + organizationId: string, + type: PolicyType, + request: PolicyRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/policies/" + type, + request, + true, + true + ); + return new PolicyResponse(r); + } + + // Organization User APIs + + async getOrganizationUser( + organizationId: string, + id: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/users/" + id, + null, + true, + true + ); + return new OrganizationUserDetailsResponse(r); + } + + async getOrganizationUserGroups(organizationId: string, id: string): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/users/" + id + "/groups", + null, + true, + true + ); + return r; + } + + async getOrganizationUsers( + organizationId: string + ): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/users", + null, + true, + true + ); + return new ListResponse(r, OrganizationUserUserDetailsResponse); + } + + async getOrganizationUserResetPasswordDetails( + organizationId: string, + id: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/users/" + id + "/reset-password-details", + null, + true, + true + ); + return new OrganizationUserResetPasswordDetailsReponse(r); + } + + async getOrganizationAutoEnrollStatus( + identifier: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + identifier + "/auto-enroll-status", + null, + true, + true + ); + return new OrganizationAutoEnrollStatusResponse(r); + } + + postOrganizationUserInvite( + organizationId: string, + request: OrganizationUserInviteRequest + ): Promise { + return this.send( + "POST", + "/organizations/" + organizationId + "/users/invite", + request, + true, + false + ); + } + + postOrganizationUserReinvite(organizationId: string, id: string): Promise { + return this.send( + "POST", + "/organizations/" + organizationId + "/users/" + id + "/reinvite", + null, + true, + false + ); + } + + async postManyOrganizationUserReinvite( + organizationId: string, + request: OrganizationUserBulkRequest + ): Promise> { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/users/reinvite", + request, + true, + true + ); + return new ListResponse(r, OrganizationUserBulkResponse); + } + + postOrganizationUserAccept( + organizationId: string, + id: string, + request: OrganizationUserAcceptRequest + ): Promise { + return this.send( + "POST", + "/organizations/" + organizationId + "/users/" + id + "/accept", + request, + true, + false + ); + } + + postOrganizationUserConfirm( + organizationId: string, + id: string, + request: OrganizationUserConfirmRequest + ): Promise { + return this.send( + "POST", + "/organizations/" + organizationId + "/users/" + id + "/confirm", + request, + true, + false + ); + } + + async postOrganizationUsersPublicKey( + organizationId: string, + request: OrganizationUserBulkRequest + ): Promise> { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/users/public-keys", + request, + true, + true + ); + return new ListResponse(r, OrganizationUserBulkPublicKeyResponse); + } + + async postOrganizationUserBulkConfirm( + organizationId: string, + request: OrganizationUserBulkConfirmRequest + ): Promise> { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/users/confirm", + request, + true, + true + ); + return new ListResponse(r, OrganizationUserBulkResponse); + } + + putOrganizationUser( + organizationId: string, + id: string, + request: OrganizationUserUpdateRequest + ): Promise { + return this.send( + "PUT", + "/organizations/" + organizationId + "/users/" + id, + request, + true, + false + ); + } + + putOrganizationUserGroups( + organizationId: string, + id: string, + request: OrganizationUserUpdateGroupsRequest + ): Promise { + return this.send( + "PUT", + "/organizations/" + organizationId + "/users/" + id + "/groups", + request, + true, + false + ); + } + + putOrganizationUserResetPasswordEnrollment( + organizationId: string, + userId: string, + request: OrganizationUserResetPasswordEnrollmentRequest + ): Promise { + return this.send( + "PUT", + "/organizations/" + organizationId + "/users/" + userId + "/reset-password-enrollment", + request, + true, + false + ); + } + + putOrganizationUserResetPassword( + organizationId: string, + id: string, + request: OrganizationUserResetPasswordRequest + ): Promise { + return this.send( + "PUT", + "/organizations/" + organizationId + "/users/" + id + "/reset-password", + request, + true, + false + ); + } + + deleteOrganizationUser(organizationId: string, id: string): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/users/" + id, + null, + true, + false + ); + } + + async deleteManyOrganizationUsers( + organizationId: string, + request: OrganizationUserBulkRequest + ): Promise> { + const r = await this.send( + "DELETE", + "/organizations/" + organizationId + "/users", + request, + true, + true + ); + return new ListResponse(r, OrganizationUserBulkResponse); + } + + // Plan APIs + + async getPlans(): Promise> { + const r = await this.send("GET", "/plans/", null, true, true); + return new ListResponse(r, PlanResponse); + } + + async postImportDirectory(organizationId: string, request: ImportDirectoryRequest): Promise { + return this.send("POST", "/organizations/" + organizationId + "/import", request, true, false); + } + + async postPublicImportDirectory(request: OrganizationImportRequest): Promise { + return this.send("POST", "/public/organization/import", request, true, false); + } + + async getTaxRates(): Promise> { + const r = await this.send("GET", "/plans/sales-tax-rates/", null, true, true); + return new ListResponse(r, TaxRateResponse); + } + + // Settings APIs + + async getSettingsDomains(): Promise { + const r = await this.send("GET", "/settings/domains", null, true, true); + return new DomainsResponse(r); + } + + async putSettingsDomains(request: UpdateDomainsRequest): Promise { + const r = await this.send("PUT", "/settings/domains", request, true, true); + return new DomainsResponse(r); + } + + // Sync APIs + + async getSync(): Promise { + const path = this.isDesktopClient || this.isWebClient ? "/sync?excludeDomains=true" : "/sync"; + const r = await this.send("GET", path, null, true, true); + return new SyncResponse(r); + } + + // Two-factor APIs + + async getTwoFactorProviders(): Promise> { + const r = await this.send("GET", "/two-factor", null, true, true); + return new ListResponse(r, TwoFactorProviderResponse); + } + + async getTwoFactorOrganizationProviders( + organizationId: string + ): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/two-factor", + null, + true, + true + ); + return new ListResponse(r, TwoFactorProviderResponse); + } + + async getTwoFactorAuthenticator( + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/two-factor/get-authenticator", request, true, true); + return new TwoFactorAuthenticatorResponse(r); + } + + async getTwoFactorEmail(request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/two-factor/get-email", request, true, true); + return new TwoFactorEmailResponse(r); + } + + async getTwoFactorDuo(request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/two-factor/get-duo", request, true, true); + return new TwoFactorDuoResponse(r); + } + + async getTwoFactorOrganizationDuo( + organizationId: string, + request: SecretVerificationRequest + ): Promise { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/two-factor/get-duo", + request, + true, + true + ); + return new TwoFactorDuoResponse(r); + } + + async getTwoFactorYubiKey(request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/two-factor/get-yubikey", request, true, true); + return new TwoFactorYubiKeyResponse(r); + } + + async getTwoFactorWebAuthn( + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/two-factor/get-webauthn", request, true, true); + return new TwoFactorWebAuthnResponse(r); + } + + async getTwoFactorWebAuthnChallenge( + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/two-factor/get-webauthn-challenge", request, true, true); + return new ChallengeResponse(r); + } + + async getTwoFactorRecover(request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/two-factor/get-recover", request, true, true); + return new TwoFactorRecoverResponse(r); + } + + async putTwoFactorAuthenticator( + request: UpdateTwoFactorAuthenticatorRequest + ): Promise { + const r = await this.send("PUT", "/two-factor/authenticator", request, true, true); + return new TwoFactorAuthenticatorResponse(r); + } + + async putTwoFactorEmail(request: UpdateTwoFactorEmailRequest): Promise { + const r = await this.send("PUT", "/two-factor/email", request, true, true); + return new TwoFactorEmailResponse(r); + } + + async putTwoFactorDuo(request: UpdateTwoFactorDuoRequest): Promise { + const r = await this.send("PUT", "/two-factor/duo", request, true, true); + return new TwoFactorDuoResponse(r); + } + + async putTwoFactorOrganizationDuo( + organizationId: string, + request: UpdateTwoFactorDuoRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/two-factor/duo", + request, + true, + true + ); + return new TwoFactorDuoResponse(r); + } + + async putTwoFactorYubiKey( + request: UpdateTwoFactorYubioOtpRequest + ): Promise { + const r = await this.send("PUT", "/two-factor/yubikey", request, true, true); + return new TwoFactorYubiKeyResponse(r); + } + + async putTwoFactorWebAuthn( + request: UpdateTwoFactorWebAuthnRequest + ): Promise { + const response = request.deviceResponse.response as AuthenticatorAttestationResponse; + const data: any = Object.assign({}, request); + + data.deviceResponse = { + id: request.deviceResponse.id, + rawId: btoa(request.deviceResponse.id), + type: request.deviceResponse.type, + extensions: request.deviceResponse.getClientExtensionResults(), + response: { + AttestationObject: Utils.fromBufferToB64(response.attestationObject), + clientDataJson: Utils.fromBufferToB64(response.clientDataJSON), + }, + }; + + const r = await this.send("PUT", "/two-factor/webauthn", data, true, true); + return new TwoFactorWebAuthnResponse(r); + } + + async deleteTwoFactorWebAuthn( + request: UpdateTwoFactorWebAuthnDeleteRequest + ): Promise { + const r = await this.send("DELETE", "/two-factor/webauthn", request, true, true); + return new TwoFactorWebAuthnResponse(r); + } + + async putTwoFactorDisable(request: TwoFactorProviderRequest): Promise { + const r = await this.send("PUT", "/two-factor/disable", request, true, true); + return new TwoFactorProviderResponse(r); + } + + async putTwoFactorOrganizationDisable( + organizationId: string, + request: TwoFactorProviderRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/two-factor/disable", + request, + true, + true + ); + return new TwoFactorProviderResponse(r); + } + + postTwoFactorRecover(request: TwoFactorRecoveryRequest): Promise { + return this.send("POST", "/two-factor/recover", request, false, false); + } + + postTwoFactorEmailSetup(request: TwoFactorEmailRequest): Promise { + return this.send("POST", "/two-factor/send-email", request, true, false); + } + + postTwoFactorEmail(request: TwoFactorEmailRequest): Promise { + return this.send("POST", "/two-factor/send-email-login", request, false, false); + } + + // Emergency Access APIs + + async getEmergencyAccessTrusted(): Promise> { + const r = await this.send("GET", "/emergency-access/trusted", null, true, true); + return new ListResponse(r, EmergencyAccessGranteeDetailsResponse); + } + + async getEmergencyAccessGranted(): Promise> { + const r = await this.send("GET", "/emergency-access/granted", null, true, true); + return new ListResponse(r, EmergencyAccessGrantorDetailsResponse); + } + + async getEmergencyAccess(id: string): Promise { + const r = await this.send("GET", "/emergency-access/" + id, null, true, true); + return new EmergencyAccessGranteeDetailsResponse(r); + } + + async getEmergencyGrantorPolicies(id: string): Promise> { + const r = await this.send("GET", "/emergency-access/" + id + "/policies", null, true, true); + return new ListResponse(r, PolicyResponse); + } + + putEmergencyAccess(id: string, request: EmergencyAccessUpdateRequest): Promise { + return this.send("PUT", "/emergency-access/" + id, request, true, false); + } + + deleteEmergencyAccess(id: string): Promise { + return this.send("DELETE", "/emergency-access/" + id, null, true, false); + } + + postEmergencyAccessInvite(request: EmergencyAccessInviteRequest): Promise { + return this.send("POST", "/emergency-access/invite", request, true, false); + } + + postEmergencyAccessReinvite(id: string): Promise { + return this.send("POST", "/emergency-access/" + id + "/reinvite", null, true, false); + } + + postEmergencyAccessAccept(id: string, request: EmergencyAccessAcceptRequest): Promise { + return this.send("POST", "/emergency-access/" + id + "/accept", request, true, false); + } + + postEmergencyAccessConfirm(id: string, request: EmergencyAccessConfirmRequest): Promise { + return this.send("POST", "/emergency-access/" + id + "/confirm", request, true, false); + } + + postEmergencyAccessInitiate(id: string): Promise { + return this.send("POST", "/emergency-access/" + id + "/initiate", null, true, false); + } + + postEmergencyAccessApprove(id: string): Promise { + return this.send("POST", "/emergency-access/" + id + "/approve", null, true, false); + } + + postEmergencyAccessReject(id: string): Promise { + return this.send("POST", "/emergency-access/" + id + "/reject", null, true, false); + } + + async postEmergencyAccessTakeover(id: string): Promise { + const r = await this.send("POST", "/emergency-access/" + id + "/takeover", null, true, true); + return new EmergencyAccessTakeoverResponse(r); + } + + async postEmergencyAccessPassword( + id: string, + request: EmergencyAccessPasswordRequest + ): Promise { + await this.send("POST", "/emergency-access/" + id + "/password", request, true, true); + } + + async postEmergencyAccessView(id: string): Promise { + const r = await this.send("POST", "/emergency-access/" + id + "/view", null, true, true); + return new EmergencyAccessViewResponse(r); + } + + // Organization APIs + + async getOrganization(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id, null, true, true); + return new OrganizationResponse(r); + } + + async getOrganizationBilling(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/billing", null, true, true); + return new BillingResponse(r); + } + + async getOrganizationSubscription(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/subscription", null, true, true); + return new OrganizationSubscriptionResponse(r); + } + + async getOrganizationLicense(id: string, installationId: string): Promise { + return this.send( + "GET", + "/organizations/" + id + "/license?installationId=" + installationId, + null, + true, + true + ); + } + + async getOrganizationTaxInfo(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/tax", null, true, true); + return new TaxInfoResponse(r); + } + + async getOrganizationSso(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/sso", null, true, true); + return new OrganizationSsoResponse(r); + } + + async postOrganization(request: OrganizationCreateRequest): Promise { + const r = await this.send("POST", "/organizations", request, true, true); + return new OrganizationResponse(r); + } + + async putOrganization( + id: string, + request: OrganizationUpdateRequest + ): Promise { + const r = await this.send("PUT", "/organizations/" + id, request, true, true); + return new OrganizationResponse(r); + } + + async putOrganizationTaxInfo( + id: string, + request: OrganizationTaxInfoUpdateRequest + ): Promise { + return this.send("PUT", "/organizations/" + id + "/tax", request, true, false); + } + + postLeaveOrganization(id: string): Promise { + return this.send("POST", "/organizations/" + id + "/leave", null, true, false); + } + + async postOrganizationLicense(data: FormData): Promise { + const r = await this.send("POST", "/organizations/license", data, true, true); + return new OrganizationResponse(r); + } + + async postOrganizationLicenseUpdate(id: string, data: FormData): Promise { + return this.send("POST", "/organizations/" + id + "/license", data, true, false); + } + + async postOrganizationApiKey( + id: string, + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/organizations/" + id + "/api-key", request, true, true); + return new ApiKeyResponse(r); + } + + async postOrganizationRotateApiKey( + id: string, + request: SecretVerificationRequest + ): Promise { + const r = await this.send( + "POST", + "/organizations/" + id + "/rotate-api-key", + request, + true, + true + ); + return new ApiKeyResponse(r); + } + + async postOrganizationSso( + id: string, + request: OrganizationSsoRequest + ): Promise { + const r = await this.send("POST", "/organizations/" + id + "/sso", request, true, true); + return new OrganizationSsoResponse(r); + } + + async postOrganizationUpgrade( + id: string, + request: OrganizationUpgradeRequest + ): Promise { + const r = await this.send("POST", "/organizations/" + id + "/upgrade", request, true, true); + return new PaymentResponse(r); + } + + async postOrganizationUpdateSubscription( + id: string, + request: OrganizationSubscriptionUpdateRequest + ): Promise { + return this.send("POST", "/organizations/" + id + "/subscription", request, true, false); + } + + async postOrganizationSeat(id: string, request: SeatRequest): Promise { + const r = await this.send("POST", "/organizations/" + id + "/seat", request, true, true); + return new PaymentResponse(r); + } + + async postOrganizationStorage(id: string, request: StorageRequest): Promise { + const r = await this.send("POST", "/organizations/" + id + "/storage", request, true, true); + return new PaymentResponse(r); + } + + postOrganizationPayment(id: string, request: PaymentRequest): Promise { + return this.send("POST", "/organizations/" + id + "/payment", request, true, false); + } + + postOrganizationVerifyBank(id: string, request: VerifyBankRequest): Promise { + return this.send("POST", "/organizations/" + id + "/verify-bank", request, true, false); + } + + postOrganizationCancel(id: string): Promise { + return this.send("POST", "/organizations/" + id + "/cancel", null, true, false); + } + + postOrganizationReinstate(id: string): Promise { + return this.send("POST", "/organizations/" + id + "/reinstate", null, true, false); + } + + deleteOrganization(id: string, request: SecretVerificationRequest): Promise { + return this.send("DELETE", "/organizations/" + id, request, true, false); + } + + async getOrganizationKeys(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/keys", null, true, true); + return new OrganizationKeysResponse(r); + } + + async postOrganizationKeys( + id: string, + request: OrganizationKeysRequest + ): Promise { + const r = await this.send("POST", "/organizations/" + id + "/keys", request, true, true); + return new OrganizationKeysResponse(r); + } + + // Provider APIs + + async postProviderSetup(id: string, request: ProviderSetupRequest) { + const r = await this.send("POST", "/providers/" + id + "/setup", request, true, true); + return new ProviderResponse(r); + } + + async getProvider(id: string) { + const r = await this.send("GET", "/providers/" + id, null, true, true); + return new ProviderResponse(r); + } + + async putProvider(id: string, request: ProviderUpdateRequest) { + const r = await this.send("PUT", "/providers/" + id, request, true, true); + return new ProviderResponse(r); + } + + // Provider User APIs + + async getProviderUsers( + providerId: string + ): Promise> { + const r = await this.send("GET", "/providers/" + providerId + "/users", null, true, true); + return new ListResponse(r, ProviderUserUserDetailsResponse); + } + + async getProviderUser(providerId: string, id: string): Promise { + const r = await this.send("GET", "/providers/" + providerId + "/users/" + id, null, true, true); + return new ProviderUserResponse(r); + } + + postProviderUserInvite(providerId: string, request: ProviderUserInviteRequest): Promise { + return this.send("POST", "/providers/" + providerId + "/users/invite", request, true, false); + } + + postProviderUserReinvite(providerId: string, id: string): Promise { + return this.send( + "POST", + "/providers/" + providerId + "/users/" + id + "/reinvite", + null, + true, + false + ); + } + + async postManyProviderUserReinvite( + providerId: string, + request: ProviderUserBulkRequest + ): Promise> { + const r = await this.send( + "POST", + "/providers/" + providerId + "/users/reinvite", + request, + true, + true + ); + return new ListResponse(r, ProviderUserBulkResponse); + } + + async postProviderUserBulkConfirm( + providerId: string, + request: ProviderUserBulkConfirmRequest + ): Promise> { + const r = await this.send( + "POST", + "/providers/" + providerId + "/users/confirm", + request, + true, + true + ); + return new ListResponse(r, ProviderUserBulkResponse); + } + + async deleteManyProviderUsers( + providerId: string, + request: ProviderUserBulkRequest + ): Promise> { + const r = await this.send("DELETE", "/providers/" + providerId + "/users", request, true, true); + return new ListResponse(r, ProviderUserBulkResponse); + } + + postProviderUserAccept( + providerId: string, + id: string, + request: ProviderUserAcceptRequest + ): Promise { + return this.send( + "POST", + "/providers/" + providerId + "/users/" + id + "/accept", + request, + true, + false + ); + } + + postProviderUserConfirm( + providerId: string, + id: string, + request: ProviderUserConfirmRequest + ): Promise { + return this.send( + "POST", + "/providers/" + providerId + "/users/" + id + "/confirm", + request, + true, + false + ); + } + + async postProviderUsersPublicKey( + providerId: string, + request: ProviderUserBulkRequest + ): Promise> { + const r = await this.send( + "POST", + "/providers/" + providerId + "/users/public-keys", + request, + true, + true + ); + return new ListResponse(r, ProviderUserBulkPublicKeyResponse); + } + + putProviderUser( + providerId: string, + id: string, + request: ProviderUserUpdateRequest + ): Promise { + return this.send("PUT", "/providers/" + providerId + "/users/" + id, request, true, false); + } + + deleteProviderUser(providerId: string, id: string): Promise { + return this.send("DELETE", "/providers/" + providerId + "/users/" + id, null, true, false); + } + + // Provider Organization APIs + + async getProviderClients( + providerId: string + ): Promise> { + const r = await this.send( + "GET", + "/providers/" + providerId + "/organizations", + null, + true, + true + ); + return new ListResponse(r, ProviderOrganizationOrganizationDetailsResponse); + } + + postProviderAddOrganization( + providerId: string, + request: ProviderAddOrganizationRequest + ): Promise { + return this.send( + "POST", + "/providers/" + providerId + "/organizations/add", + request, + true, + false + ); + } + + async postProviderCreateOrganization( + providerId: string, + request: ProviderOrganizationCreateRequest + ): Promise { + const r = await this.send( + "POST", + "/providers/" + providerId + "/organizations", + request, + true, + true + ); + return new ProviderOrganizationResponse(r); + } + + deleteProviderOrganization(providerId: string, id: string): Promise { + return this.send( + "DELETE", + "/providers/" + providerId + "/organizations/" + id, + null, + true, + false + ); + } + + // Event APIs + + async getEvents(start: string, end: string, token: string): Promise> { + const r = await this.send( + "GET", + this.addEventParameters("/events", start, end, token), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsCipher( + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters("/ciphers/" + id + "/events", start, end, token), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsOrganization( + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters("/organizations/" + id + "/events", start, end, token), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsOrganizationUser( + organizationId: string, + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters( + "/organizations/" + organizationId + "/users/" + id + "/events", + start, + end, + token + ), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsProvider( + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters("/providers/" + id + "/events", start, end, token), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsProviderUser( + providerId: string, + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters( + "/providers/" + providerId + "/users/" + id + "/events", + start, + end, + token + ), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async postEventsCollect(request: EventRequest[]): Promise { + const authHeader = await this.getActiveBearerToken(); + const headers = new Headers({ + "Device-Type": this.deviceType, + Authorization: "Bearer " + authHeader, + "Content-Type": "application/json; charset=utf-8", + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + const response = await this.fetch( + new Request(this.environmentService.getEventsUrl() + "/collect", { + cache: "no-store", + credentials: this.getCredentials(), + method: "POST", + body: JSON.stringify(request), + headers: headers, + }) + ); + if (response.status !== 200) { + return Promise.reject("Event post failed."); + } + } + + // User APIs + + async getUserPublicKey(id: string): Promise { + const r = await this.send("GET", "/users/" + id + "/public-key", null, true, true); + return new UserKeyResponse(r); + } + + // HIBP APIs + + async getHibpBreach(username: string): Promise { + const r = await this.send("GET", "/hibp/breach?username=" + username, null, true, true); + return r.map((a: any) => new BreachAccountResponse(a)); + } + + // Misc + + async postBitPayInvoice(request: BitPayInvoiceRequest): Promise { + const r = await this.send("POST", "/bitpay-invoice", request, true, true); + return r as string; + } + + async postSetupPayment(): Promise { + const r = await this.send("POST", "/setup-payment", null, true, true); + return r as string; + } + + // Key Connector + + async getUserKeyFromKeyConnector(keyConnectorUrl: string): Promise { + const authHeader = await this.getActiveBearerToken(); + + const response = await this.fetch( + new Request(keyConnectorUrl + "/user-keys", { + cache: "no-store", + method: "GET", + headers: new Headers({ + Accept: "application/json", + Authorization: "Bearer " + authHeader, + }), + }) + ); + + if (response.status !== 200) { + const error = await this.handleError(response, false, true); + return Promise.reject(error); + } + + return new KeyConnectorUserKeyResponse(await response.json()); + } + + async postUserKeyToKeyConnector( + keyConnectorUrl: string, + request: KeyConnectorUserKeyRequest + ): Promise { + const authHeader = await this.getActiveBearerToken(); + + const response = await this.fetch( + new Request(keyConnectorUrl + "/user-keys", { + cache: "no-store", + method: "POST", + headers: new Headers({ + Accept: "application/json", + Authorization: "Bearer " + authHeader, + "Content-Type": "application/json; charset=utf-8", + }), + body: JSON.stringify(request), + }) + ); + + if (response.status !== 200) { + const error = await this.handleError(response, false, true); + return Promise.reject(error); + } + } + + async getKeyConnectorAlive(keyConnectorUrl: string) { + const response = await this.fetch( + new Request(keyConnectorUrl + "/alive", { + cache: "no-store", + method: "GET", + headers: new Headers({ + Accept: "application/json", + "Content-Type": "application/json; charset=utf-8", + }), + }) + ); + + if (response.status !== 200) { + const error = await this.handleError(response, false, true); + return Promise.reject(error); + } + } + + // Helpers + + async getActiveBearerToken(): Promise { + let accessToken = await this.tokenService.getToken(); + if (await this.tokenService.tokenNeedsRefresh()) { + await this.doAuthRefresh(); + accessToken = await this.tokenService.getToken(); + } + return accessToken; + } + + async fetch(request: Request): Promise { + if (request.method === "GET") { + request.headers.set("Cache-Control", "no-store"); + request.headers.set("Pragma", "no-cache"); + } + request.headers.set("Bitwarden-Client-Name", this.platformUtilsService.getClientType()); + request.headers.set( + "Bitwarden-Client-Version", + await this.platformUtilsService.getApplicationVersion() + ); + return this.nativeFetch(request); + } + + nativeFetch(request: Request): Promise { + return fetch(request); + } + + async preValidateSso(identifier: string): Promise { + if (identifier == null || identifier === "") { + throw new Error("Organization Identifier was not provided."); + } + const headers = new Headers({ + Accept: "application/json", + "Device-Type": this.deviceType, + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + + const path = `/account/prevalidate?domainHint=${encodeURIComponent(identifier)}`; + const response = await this.fetch( + new Request(this.environmentService.getIdentityUrl() + path, { + cache: "no-store", + credentials: this.getCredentials(), + headers: headers, + method: "GET", + }) + ); + + if (response.status === 200) { + return true; + } else { + const error = await this.handleError(response, false, true); + return Promise.reject(error); + } + } + + async postCreateSponsorship( + sponsoredOrgId: string, + request: OrganizationSponsorshipCreateRequest + ): Promise { + return await this.send( + "POST", + "/organization/sponsorship/" + sponsoredOrgId + "/families-for-enterprise", + request, + true, + false + ); + } + + async deleteRevokeSponsorship(sponsoringOrganizationId: string): Promise { + return await this.send( + "DELETE", + "/organization/sponsorship/" + sponsoringOrganizationId, + null, + true, + false + ); + } + + async deleteRemoveSponsorship(sponsoringOrgId: string): Promise { + return await this.send( + "DELETE", + "/organization/sponsorship/sponsored/" + sponsoringOrgId, + null, + true, + false + ); + } + + async postPreValidateSponsorshipToken(sponsorshipToken: string): Promise { + const r = await this.send( + "POST", + "/organization/sponsorship/validate-token?sponsorshipToken=" + + encodeURIComponent(sponsorshipToken), + null, + true, + true + ); + return r as boolean; + } + + async postRedeemSponsorship( + sponsorshipToken: string, + request: OrganizationSponsorshipRedeemRequest + ): Promise { + return await this.send( + "POST", + "/organization/sponsorship/redeem?sponsorshipToken=" + encodeURIComponent(sponsorshipToken), + request, + true, + false + ); + } + + async postResendSponsorshipOffer(sponsoringOrgId: string): Promise { + return await this.send( + "POST", + "/organization/sponsorship/" + sponsoringOrgId + "/families-for-enterprise/resend", + null, + true, + false + ); + } + + protected async doAuthRefresh(): Promise { + const refreshToken = await this.tokenService.getRefreshToken(); + if (refreshToken != null && refreshToken !== "") { + return this.doRefreshToken(); + } + + const clientId = await this.tokenService.getClientId(); + const clientSecret = await this.tokenService.getClientSecret(); + if (!Utils.isNullOrWhitespace(clientId) && !Utils.isNullOrWhitespace(clientSecret)) { + return this.doApiTokenRefresh(); + } + + throw new Error("Cannot refresh token, no refresh token or api keys are stored"); + } + + protected async doRefreshToken(): Promise { + const refreshToken = await this.tokenService.getRefreshToken(); + if (refreshToken == null || refreshToken === "") { + throw new Error(); + } + const headers = new Headers({ + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + Accept: "application/json", + "Device-Type": this.deviceType, + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + + const decodedToken = await this.tokenService.decodeToken(); + const response = await this.fetch( + new Request(this.environmentService.getIdentityUrl() + "/connect/token", { + body: this.qsStringify({ + grant_type: "refresh_token", + client_id: decodedToken.client_id, + refresh_token: refreshToken, + }), + cache: "no-store", + credentials: this.getCredentials(), + headers: headers, + method: "POST", + }) + ); + + if (response.status === 200) { + const responseJson = await response.json(); + const tokenResponse = new IdentityTokenResponse(responseJson); + await this.tokenService.setTokens( + tokenResponse.accessToken, + tokenResponse.refreshToken, + null + ); + } else { + const error = await this.handleError(response, true, true); + return Promise.reject(error); + } + } + + protected async doApiTokenRefresh(): Promise { + const clientId = await this.tokenService.getClientId(); + const clientSecret = await this.tokenService.getClientSecret(); + + const appId = await this.appIdService.getAppId(); + const deviceRequest = new DeviceRequest(appId, this.platformUtilsService); + + const tokenRequest = new ApiTokenRequest( + clientId, + clientSecret, + new TokenRequestTwoFactor(), + deviceRequest + ); + + const response = await this.postIdentityToken(tokenRequest); + if (!(response instanceof IdentityTokenResponse)) { + throw new Error("Invalid response received when refreshing api token"); + } + + await this.tokenService.setToken(response.accessToken); + } + + private async send( + method: "GET" | "POST" | "PUT" | "DELETE", + path: string, + body: any, + authed: boolean, + hasResponse: boolean, + apiUrl?: string, + alterHeaders?: (headers: Headers) => void + ): Promise { + apiUrl = Utils.isNullOrWhitespace(apiUrl) ? this.environmentService.getApiUrl() : apiUrl; + + const requestUrl = apiUrl + path; + // Prevent directory traversal from malicious paths + if (new URL(requestUrl).href !== requestUrl) { + return Promise.reject("Invalid request url path."); + } + + const headers = new Headers({ + "Device-Type": this.deviceType, + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + + const requestInit: RequestInit = { + cache: "no-store", + credentials: this.getCredentials(), + method: method, + }; + + if (authed) { + const authHeader = await this.getActiveBearerToken(); + headers.set("Authorization", "Bearer " + authHeader); + } + if (body != null) { + if (typeof body === "string") { + requestInit.body = body; + headers.set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); + } else if (typeof body === "object") { + if (body instanceof FormData) { + requestInit.body = body; + } else { + headers.set("Content-Type", "application/json; charset=utf-8"); + requestInit.body = JSON.stringify(body); + } + } + } + if (hasResponse) { + headers.set("Accept", "application/json"); + } + if (alterHeaders != null) { + alterHeaders(headers); + } + + requestInit.headers = headers; + const response = await this.fetch(new Request(requestUrl, requestInit)); + + if (hasResponse && response.status === 200) { + const responseJson = await response.json(); + return responseJson; + } else if (response.status !== 200) { + const error = await this.handleError(response, false, authed); + return Promise.reject(error); + } + } + + private async handleError( + response: Response, + tokenError: boolean, + authed: boolean + ): Promise { + if ( + authed && + ((tokenError && response.status === 400) || + response.status === 401 || + response.status === 403) + ) { + await this.logoutCallback(true); + return null; + } + + let responseJson: any = null; + if (this.isJsonResponse(response)) { + responseJson = await response.json(); + } else if (this.isTextResponse(response)) { + responseJson = { Message: await response.text() }; + } + + return new ErrorResponse(responseJson, response.status, tokenError); + } + + private qsStringify(params: any): string { + return Object.keys(params) + .map((key) => { + return encodeURIComponent(key) + "=" + encodeURIComponent(params[key]); + }) + .join("&"); + } + + private getCredentials(): RequestCredentials { + if (!this.isWebClient || this.environmentService.hasBaseUrl()) { + return "include"; + } + return undefined; + } + + private addEventParameters(base: string, start: string, end: string, token: string) { + if (start != null) { + base += "?start=" + start; + } + if (end != null) { + base += base.indexOf("?") > -1 ? "&" : "?"; + base += "end=" + end; + } + if (token != null) { + base += base.indexOf("?") > -1 ? "&" : "?"; + base += "continuationToken=" + token; + } + return base; + } + + private isJsonResponse(response: Response): boolean { + const typeHeader = response.headers.get("content-type"); + return typeHeader != null && typeHeader.indexOf("application/json") > -1; + } + + private isTextResponse(response: Response): boolean { + const typeHeader = response.headers.get("content-type"); + return typeHeader != null && typeHeader.indexOf("text") > -1; + } +} diff --git a/jslib/common/src/services/appId.service.ts b/jslib/common/src/services/appId.service.ts new file mode 100644 index 00000000..a6406fdc --- /dev/null +++ b/jslib/common/src/services/appId.service.ts @@ -0,0 +1,31 @@ +import { AppIdService as AppIdServiceAbstraction } from "../abstractions/appId.service"; +import { StorageService } from "../abstractions/storage.service"; +import { HtmlStorageLocation } from "../enums/htmlStorageLocation"; +import { Utils } from "../misc/utils"; + +export class AppIdService implements AppIdServiceAbstraction { + constructor(private storageService: StorageService) {} + + getAppId(): Promise { + return this.makeAndGetAppId("appId"); + } + + getAnonymousAppId(): Promise { + return this.makeAndGetAppId("anonymousAppId"); + } + + private async makeAndGetAppId(key: string) { + const existingId = await this.storageService.get(key, { + htmlStorageLocation: HtmlStorageLocation.Local, + }); + if (existingId != null) { + return existingId; + } + + const guid = Utils.newGuid(); + await this.storageService.save(key, guid, { + htmlStorageLocation: HtmlStorageLocation.Local, + }); + return guid; + } +} diff --git a/jslib/common/src/services/audit.service.ts b/jslib/common/src/services/audit.service.ts new file mode 100644 index 00000000..4ceebf67 --- /dev/null +++ b/jslib/common/src/services/audit.service.ts @@ -0,0 +1,44 @@ +import { ApiService } from "../abstractions/api.service"; +import { AuditService as AuditServiceAbstraction } from "../abstractions/audit.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { throttle } from "../misc/throttle"; +import { Utils } from "../misc/utils"; +import { BreachAccountResponse } from "../models/response/breachAccountResponse"; +import { ErrorResponse } from "../models/response/errorResponse"; + +const PwnedPasswordsApi = "https://api.pwnedpasswords.com/range/"; + +export class AuditService implements AuditServiceAbstraction { + constructor( + private cryptoFunctionService: CryptoFunctionService, + private apiService: ApiService + ) {} + + @throttle(100, () => "passwordLeaked") + async passwordLeaked(password: string): Promise { + const hashBytes = await this.cryptoFunctionService.hash(password, "sha1"); + const hash = Utils.fromBufferToHex(hashBytes).toUpperCase(); + const hashStart = hash.substr(0, 5); + const hashEnding = hash.substr(5); + + const response = await this.apiService.nativeFetch(new Request(PwnedPasswordsApi + hashStart)); + const leakedHashes = await response.text(); + const match = leakedHashes.split(/\r?\n/).find((v) => { + return v.split(":")[0] === hashEnding; + }); + + return match != null ? parseInt(match.split(":")[1], 10) : 0; + } + + async breachedAccounts(username: string): Promise { + try { + return await this.apiService.getHibpBreach(username); + } catch (e) { + const error = e as ErrorResponse; + if (error.statusCode === 404) { + return []; + } + throw new Error(); + } + } +} diff --git a/jslib/common/src/services/auth.service.ts b/jslib/common/src/services/auth.service.ts new file mode 100644 index 00000000..fe4542b8 --- /dev/null +++ b/jslib/common/src/services/auth.service.ts @@ -0,0 +1,198 @@ +import { ApiService } from "../abstractions/api.service"; +import { AppIdService } from "../abstractions/appId.service"; +import { AuthService as AuthServiceAbstraction } from "../abstractions/auth.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { EnvironmentService } from "../abstractions/environment.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { KeyConnectorService } from "../abstractions/keyConnector.service"; +import { LogService } from "../abstractions/log.service"; +import { MessagingService } from "../abstractions/messaging.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { StateService } from "../abstractions/state.service"; +import { TokenService } from "../abstractions/token.service"; +import { TwoFactorService } from "../abstractions/twoFactor.service"; +import { AuthenticationType } from "../enums/authenticationType"; +import { KdfType } from "../enums/kdfType"; +import { ApiLogInStrategy } from "../misc/logInStrategies/apiLogin.strategy"; +import { PasswordLogInStrategy } from "../misc/logInStrategies/passwordLogin.strategy"; +import { SsoLogInStrategy } from "../misc/logInStrategies/ssoLogin.strategy"; +import { AuthResult } from "../models/domain/authResult"; +import { + ApiLogInCredentials, + PasswordLogInCredentials, + SsoLogInCredentials, +} from "../models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor"; +import { PreloginRequest } from "../models/request/preloginRequest"; +import { ErrorResponse } from "../models/response/errorResponse"; + +const sessionTimeoutLength = 2 * 60 * 1000; // 2 minutes + +export class AuthService implements AuthServiceAbstraction { + get email(): string { + return this.logInStrategy instanceof PasswordLogInStrategy ? this.logInStrategy.email : null; + } + + get masterPasswordHash(): string { + return this.logInStrategy instanceof PasswordLogInStrategy + ? this.logInStrategy.masterPasswordHash + : null; + } + + private logInStrategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; + private sessionTimeout: any; + + constructor( + protected cryptoService: CryptoService, + protected apiService: ApiService, + protected tokenService: TokenService, + protected appIdService: AppIdService, + protected platformUtilsService: PlatformUtilsService, + protected messagingService: MessagingService, + protected logService: LogService, + protected keyConnectorService: KeyConnectorService, + protected environmentService: EnvironmentService, + protected stateService: StateService, + protected twoFactorService: TwoFactorService, + protected i18nService: I18nService + ) {} + + async logIn( + credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + ): Promise { + this.clearState(); + + let strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; + + if (credentials.type === AuthenticationType.Password) { + strategy = new PasswordLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this + ); + } else if (credentials.type === AuthenticationType.Sso) { + strategy = new SsoLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this.keyConnectorService + ); + } else if (credentials.type === AuthenticationType.Api) { + strategy = new ApiLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this.environmentService, + this.keyConnectorService + ); + } + + const result = await strategy.logIn(credentials as any); + + if (result?.requiresTwoFactor) { + this.saveState(strategy); + } + return result; + } + + async logInTwoFactor( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string + ): Promise { + if (this.logInStrategy == null) { + throw new Error(this.i18nService.t("sessionTimeout")); + } + + try { + const result = await this.logInStrategy.logInTwoFactor(twoFactor, captchaResponse); + + // Only clear state if 2FA token has been accepted, otherwise we need to be able to try again + if (!result.requiresTwoFactor && !result.requiresCaptcha) { + this.clearState(); + } + return result; + } catch (e) { + // API exceptions are okay, but if there are any unhandled client-side errors then clear state to be safe + if (!(e instanceof ErrorResponse)) { + this.clearState(); + } + throw e; + } + } + + logOut(callback: () => void) { + callback(); + this.messagingService.send("loggedOut"); + } + + authingWithApiKey(): boolean { + return this.logInStrategy instanceof ApiLogInStrategy; + } + + authingWithSso(): boolean { + return this.logInStrategy instanceof SsoLogInStrategy; + } + + authingWithPassword(): boolean { + return this.logInStrategy instanceof PasswordLogInStrategy; + } + + async makePreloginKey(masterPassword: string, email: string): Promise { + email = email.trim().toLowerCase(); + let kdf: KdfType = null; + let kdfIterations: number = null; + try { + const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email)); + if (preloginResponse != null) { + kdf = preloginResponse.kdf; + kdfIterations = preloginResponse.kdfIterations; + } + } catch (e) { + if (e == null || e.statusCode !== 404) { + throw e; + } + } + return this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations); + } + + private saveState(strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy) { + this.logInStrategy = strategy; + this.startSessionTimeout(); + } + + private clearState() { + this.logInStrategy = null; + this.clearSessionTimeout(); + } + + private startSessionTimeout() { + this.clearSessionTimeout(); + this.sessionTimeout = setTimeout(() => this.clearState(), sessionTimeoutLength); + } + + private clearSessionTimeout() { + if (this.sessionTimeout != null) { + clearTimeout(this.sessionTimeout); + } + } +} diff --git a/jslib/common/src/services/azureFileUpload.service.ts b/jslib/common/src/services/azureFileUpload.service.ts new file mode 100644 index 00000000..d7017bfb --- /dev/null +++ b/jslib/common/src/services/azureFileUpload.service.ts @@ -0,0 +1,214 @@ +import { LogService } from "../abstractions/log.service"; +import { Utils } from "../misc/utils"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; + +const MAX_SINGLE_BLOB_UPLOAD_SIZE = 256 * 1024 * 1024; // 256 MiB +const MAX_BLOCKS_PER_BLOB = 50000; + +export class AzureFileUploadService { + constructor(private logService: LogService) {} + + async upload(url: string, data: EncArrayBuffer, renewalCallback: () => Promise) { + if (data.buffer.byteLength <= MAX_SINGLE_BLOB_UPLOAD_SIZE) { + return await this.azureUploadBlob(url, data); + } else { + return await this.azureUploadBlocks(url, data, renewalCallback); + } + } + private async azureUploadBlob(url: string, data: EncArrayBuffer) { + const urlObject = Utils.getUrl(url); + const headers = new Headers({ + "x-ms-date": new Date().toUTCString(), + "x-ms-version": urlObject.searchParams.get("sv"), + "Content-Length": data.buffer.byteLength.toString(), + "x-ms-blob-type": "BlockBlob", + }); + + const request = new Request(url, { + body: data.buffer, + cache: "no-store", + method: "PUT", + headers: headers, + }); + + const blobResponse = await fetch(request); + + if (blobResponse.status !== 201) { + throw new Error(`Failed to create Azure blob: ${blobResponse.status}`); + } + } + private async azureUploadBlocks( + url: string, + data: EncArrayBuffer, + renewalCallback: () => Promise + ) { + const baseUrl = Utils.getUrl(url); + const blockSize = this.getMaxBlockSize(baseUrl.searchParams.get("sv")); + let blockIndex = 0; + const numBlocks = Math.ceil(data.buffer.byteLength / blockSize); + const blocksStaged: string[] = []; + + if (numBlocks > MAX_BLOCKS_PER_BLOB) { + throw new Error( + `Cannot upload file, exceeds maximum size of ${blockSize * MAX_BLOCKS_PER_BLOB}` + ); + } + + // eslint-disable-next-line + try { + while (blockIndex < numBlocks) { + url = await this.renewUrlIfNecessary(url, renewalCallback); + const blockUrl = Utils.getUrl(url); + const blockId = this.encodedBlockId(blockIndex); + blockUrl.searchParams.append("comp", "block"); + blockUrl.searchParams.append("blockid", blockId); + const start = blockIndex * blockSize; + const blockData = data.buffer.slice(start, start + blockSize); + const blockHeaders = new Headers({ + "x-ms-date": new Date().toUTCString(), + "x-ms-version": blockUrl.searchParams.get("sv"), + "Content-Length": blockData.byteLength.toString(), + }); + + const blockRequest = new Request(blockUrl.toString(), { + body: blockData, + cache: "no-store", + method: "PUT", + headers: blockHeaders, + }); + + const blockResponse = await fetch(blockRequest); + + if (blockResponse.status !== 201) { + const message = `Unsuccessful block PUT. Received status ${blockResponse.status}`; + this.logService.error(message + "\n" + (await blockResponse.json())); + throw new Error(message); + } + + blocksStaged.push(blockId); + blockIndex++; + } + + url = await this.renewUrlIfNecessary(url, renewalCallback); + const blockListUrl = Utils.getUrl(url); + const blockListXml = this.blockListXml(blocksStaged); + blockListUrl.searchParams.append("comp", "blocklist"); + const headers = new Headers({ + "x-ms-date": new Date().toUTCString(), + "x-ms-version": blockListUrl.searchParams.get("sv"), + "Content-Length": blockListXml.length.toString(), + }); + + const request = new Request(blockListUrl.toString(), { + body: blockListXml, + cache: "no-store", + method: "PUT", + headers: headers, + }); + + const response = await fetch(request); + + if (response.status !== 201) { + const message = `Unsuccessful block list PUT. Received status ${response.status}`; + this.logService.error(message + "\n" + (await response.json())); + throw new Error(message); + } + } catch (e) { + throw e; + } + } + + private async renewUrlIfNecessary( + url: string, + renewalCallback: () => Promise + ): Promise { + const urlObject = Utils.getUrl(url); + const expiry = new Date(urlObject.searchParams.get("se") ?? ""); + + if (isNaN(expiry.getTime())) { + expiry.setTime(Date.now() + 3600000); + } + + if (expiry.getTime() < Date.now() + 1000) { + return await renewalCallback(); + } + return url; + } + + private encodedBlockId(blockIndex: number) { + // Encoded blockId max size is 64, so pre-encoding max size is 48 + const utfBlockId = ( + "000000000000000000000000000000000000000000000000" + blockIndex.toString() + ).slice(-48); + return Utils.fromUtf8ToB64(utfBlockId); + } + + private blockListXml(blockIdList: string[]) { + let xml = ''; + blockIdList.forEach((blockId) => { + xml += `${blockId}`; + }); + xml += ""; + return xml; + } + + private getMaxBlockSize(version: string) { + if (Version.compare(version, "2019-12-12") >= 0) { + return 4000 * 1024 * 1024; // 4000 MiB + } else if (Version.compare(version, "2016-05-31") >= 0) { + return 100 * 1024 * 1024; // 100 MiB + } else { + return 4 * 1024 * 1024; // 4 MiB + } + } +} + +class Version { + /** + * Compares two Azure Versions against each other + * @param a Version to compare + * @param b Version to compare + * @returns a number less than zero if b is newer than a, 0 if equal, + * and greater than zero if a is newer than b + */ + static compare(a: Required | string, b: Required | string) { + if (typeof a === "string") { + a = new Version(a); + } + + if (typeof b === "string") { + b = new Version(b); + } + + return a.year !== b.year + ? a.year - b.year + : a.month !== b.month + ? a.month - b.month + : a.day !== b.day + ? a.day - b.day + : 0; + } + year = 0; + month = 0; + day = 0; + + constructor(version: string) { + try { + const parts = version.split("-").map((v) => Number.parseInt(v, 10)); + this.year = parts[0]; + this.month = parts[1]; + this.day = parts[2]; + } catch { + // Ignore error + } + } + /** + * Compares two Azure Versions against each other + * @param compareTo Version to compare against + * @returns a number less than zero if compareTo is newer, 0 if equal, + * and greater than zero if this is greater than compareTo + */ + compare(compareTo: Required | string) { + return Version.compare(this, compareTo); + } +} diff --git a/jslib/common/src/services/bitwardenFileUpload.service.ts b/jslib/common/src/services/bitwardenFileUpload.service.ts new file mode 100644 index 00000000..dd189e3e --- /dev/null +++ b/jslib/common/src/services/bitwardenFileUpload.service.ts @@ -0,0 +1,34 @@ +import { ApiService } from "../abstractions/api.service"; +import { Utils } from "../misc/utils"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; + +export class BitwardenFileUploadService { + constructor(private apiService: ApiService) {} + + async upload( + encryptedFileName: string, + encryptedFileData: EncArrayBuffer, + apiCall: (fd: FormData) => Promise + ) { + const fd = new FormData(); + try { + const blob = new Blob([encryptedFileData.buffer], { type: "application/octet-stream" }); + fd.append("data", blob, encryptedFileName); + } catch (e) { + if (Utils.isNode && !Utils.isBrowser) { + fd.append( + "data", + Buffer.from(encryptedFileData.buffer) as any, + { + filepath: encryptedFileName, + contentType: "application/octet-stream", + } as any + ); + } else { + throw e; + } + } + + await apiCall(fd); + } +} diff --git a/jslib/common/src/services/broadcaster.service.ts b/jslib/common/src/services/broadcaster.service.ts new file mode 100644 index 00000000..999fbe4b --- /dev/null +++ b/jslib/common/src/services/broadcaster.service.ts @@ -0,0 +1,28 @@ +import { BroadcasterService as BroadcasterServiceAbstraction } from "../abstractions/broadcaster.service"; + +export class BroadcasterService implements BroadcasterServiceAbstraction { + subscribers: Map any> = new Map any>(); + + send(message: any, id?: string) { + if (id != null) { + if (this.subscribers.has(id)) { + this.subscribers.get(id)(message); + } + return; + } + + this.subscribers.forEach((value) => { + value(message); + }); + } + + subscribe(id: string, messageCallback: (message: any) => any) { + this.subscribers.set(id, messageCallback); + } + + unsubscribe(id: string) { + if (this.subscribers.has(id)) { + this.subscribers.delete(id); + } + } +} diff --git a/jslib/common/src/services/cipher.service.ts b/jslib/common/src/services/cipher.service.ts new file mode 100644 index 00000000..1deeec8e --- /dev/null +++ b/jslib/common/src/services/cipher.service.ts @@ -0,0 +1,1282 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService as CipherServiceAbstraction } from "../abstractions/cipher.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FileUploadService } from "../abstractions/fileUpload.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { LogService } from "../abstractions/log.service"; +import { SearchService } from "../abstractions/search.service"; +import { SettingsService } from "../abstractions/settings.service"; +import { StateService } from "../abstractions/state.service"; +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { sequentialize } from "../misc/sequentialize"; +import { Utils } from "../misc/utils"; +import { CipherData } from "../models/data/cipherData"; +import { Attachment } from "../models/domain/attachment"; +import { Card } from "../models/domain/card"; +import { Cipher } from "../models/domain/cipher"; +import Domain from "../models/domain/domainBase"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { Field } from "../models/domain/field"; +import { Identity } from "../models/domain/identity"; +import { Login } from "../models/domain/login"; +import { LoginUri } from "../models/domain/loginUri"; +import { Password } from "../models/domain/password"; +import { SecureNote } from "../models/domain/secureNote"; +import { SortedCiphersCache } from "../models/domain/sortedCiphersCache"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { AttachmentRequest } from "../models/request/attachmentRequest"; +import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest"; +import { CipherBulkMoveRequest } from "../models/request/cipherBulkMoveRequest"; +import { CipherBulkRestoreRequest } from "../models/request/cipherBulkRestoreRequest"; +import { CipherBulkShareRequest } from "../models/request/cipherBulkShareRequest"; +import { CipherCollectionsRequest } from "../models/request/cipherCollectionsRequest"; +import { CipherCreateRequest } from "../models/request/cipherCreateRequest"; +import { CipherRequest } from "../models/request/cipherRequest"; +import { CipherShareRequest } from "../models/request/cipherShareRequest"; +import { CipherResponse } from "../models/response/cipherResponse"; +import { ErrorResponse } from "../models/response/errorResponse"; +import { AttachmentView } from "../models/view/attachmentView"; +import { CipherView } from "../models/view/cipherView"; +import { FieldView } from "../models/view/fieldView"; +import { PasswordHistoryView } from "../models/view/passwordHistoryView"; +import { View } from "../models/view/view"; + +const DomainMatchBlacklist = new Map>([ + ["google.com", new Set(["script.google.com"])], +]); + +export class CipherService implements CipherServiceAbstraction { + private sortedCiphersCache: SortedCiphersCache = new SortedCiphersCache( + this.sortCiphersByLastUsed + ); + + constructor( + private cryptoService: CryptoService, + private settingsService: SettingsService, + private apiService: ApiService, + private fileUploadService: FileUploadService, + private i18nService: I18nService, + private searchService: () => SearchService, + private logService: LogService, + private stateService: StateService + ) {} + + async getDecryptedCipherCache(): Promise { + const decryptedCiphers = await this.stateService.getDecryptedCiphers(); + return decryptedCiphers; + } + + async setDecryptedCipherCache(value: CipherView[]) { + await this.stateService.setDecryptedCiphers(value); + if (this.searchService != null) { + if (value == null) { + this.searchService().clearIndex(); + } else { + this.searchService().indexCiphers(); + } + } + } + + async clearCache(userId?: string): Promise { + await this.clearDecryptedCiphersState(userId); + } + + async encrypt( + model: CipherView, + key?: SymmetricCryptoKey, + originalCipher: Cipher = null + ): Promise { + // Adjust password history + if (model.id != null) { + if (originalCipher == null) { + originalCipher = await this.get(model.id); + } + if (originalCipher != null) { + const existingCipher = await originalCipher.decrypt(); + model.passwordHistory = existingCipher.passwordHistory || []; + if (model.type === CipherType.Login && existingCipher.type === CipherType.Login) { + if ( + existingCipher.login.password != null && + existingCipher.login.password !== "" && + existingCipher.login.password !== model.login.password + ) { + const ph = new PasswordHistoryView(); + ph.password = existingCipher.login.password; + ph.lastUsedDate = model.login.passwordRevisionDate = new Date(); + model.passwordHistory.splice(0, 0, ph); + } else { + model.login.passwordRevisionDate = existingCipher.login.passwordRevisionDate; + } + } + if (existingCipher.hasFields) { + const existingHiddenFields = existingCipher.fields.filter( + (f) => + f.type === FieldType.Hidden && + f.name != null && + f.name !== "" && + f.value != null && + f.value !== "" + ); + const hiddenFields = + model.fields == null + ? [] + : model.fields.filter( + (f) => f.type === FieldType.Hidden && f.name != null && f.name !== "" + ); + existingHiddenFields.forEach((ef) => { + const matchedField = hiddenFields.find((f) => f.name === ef.name); + if (matchedField == null || matchedField.value !== ef.value) { + const ph = new PasswordHistoryView(); + ph.password = ef.name + ": " + ef.value; + ph.lastUsedDate = new Date(); + model.passwordHistory.splice(0, 0, ph); + } + }); + } + } + if (model.passwordHistory != null && model.passwordHistory.length === 0) { + model.passwordHistory = null; + } else if (model.passwordHistory != null && model.passwordHistory.length > 5) { + // only save last 5 history + model.passwordHistory = model.passwordHistory.slice(0, 5); + } + } + + const cipher = new Cipher(); + cipher.id = model.id; + cipher.folderId = model.folderId; + cipher.favorite = model.favorite; + cipher.organizationId = model.organizationId; + cipher.type = model.type; + cipher.collectionIds = model.collectionIds; + cipher.revisionDate = model.revisionDate; + cipher.reprompt = model.reprompt; + + if (key == null && cipher.organizationId != null) { + key = await this.cryptoService.getOrgKey(cipher.organizationId); + if (key == null) { + throw new Error("Cannot encrypt cipher for organization. No key."); + } + } + await Promise.all([ + this.encryptObjProperty( + model, + cipher, + { + name: null, + notes: null, + }, + key + ), + this.encryptCipherData(cipher, model, key), + this.encryptFields(model.fields, key).then((fields) => { + cipher.fields = fields; + }), + this.encryptPasswordHistories(model.passwordHistory, key).then((ph) => { + cipher.passwordHistory = ph; + }), + this.encryptAttachments(model.attachments, key).then((attachments) => { + cipher.attachments = attachments; + }), + ]); + + return cipher; + } + + async encryptAttachments( + attachmentsModel: AttachmentView[], + key: SymmetricCryptoKey + ): Promise { + if (attachmentsModel == null || attachmentsModel.length === 0) { + return null; + } + + const promises: Promise[] = []; + const encAttachments: Attachment[] = []; + attachmentsModel.forEach(async (model) => { + const attachment = new Attachment(); + attachment.id = model.id; + attachment.size = model.size; + attachment.sizeName = model.sizeName; + attachment.url = model.url; + const promise = this.encryptObjProperty( + model, + attachment, + { + fileName: null, + }, + key + ).then(async () => { + if (model.key != null) { + attachment.key = await this.cryptoService.encrypt(model.key.key, key); + } + encAttachments.push(attachment); + }); + promises.push(promise); + }); + + await Promise.all(promises); + return encAttachments; + } + + async encryptFields(fieldsModel: FieldView[], key: SymmetricCryptoKey): Promise { + if (!fieldsModel || !fieldsModel.length) { + return null; + } + + const self = this; + const encFields: Field[] = []; + await fieldsModel.reduce(async (promise, field) => { + await promise; + const encField = await self.encryptField(field, key); + encFields.push(encField); + }, Promise.resolve()); + + return encFields; + } + + async encryptField(fieldModel: FieldView, key: SymmetricCryptoKey): Promise { + const field = new Field(); + field.type = fieldModel.type; + field.linkedId = fieldModel.linkedId; + // normalize boolean type field values + if (fieldModel.type === FieldType.Boolean && fieldModel.value !== "true") { + fieldModel.value = "false"; + } + + await this.encryptObjProperty( + fieldModel, + field, + { + name: null, + value: null, + }, + key + ); + + return field; + } + + async encryptPasswordHistories( + phModels: PasswordHistoryView[], + key: SymmetricCryptoKey + ): Promise { + if (!phModels || !phModels.length) { + return null; + } + + const self = this; + const encPhs: Password[] = []; + await phModels.reduce(async (promise, ph) => { + await promise; + const encPh = await self.encryptPasswordHistory(ph, key); + encPhs.push(encPh); + }, Promise.resolve()); + + return encPhs; + } + + async encryptPasswordHistory( + phModel: PasswordHistoryView, + key: SymmetricCryptoKey + ): Promise { + const ph = new Password(); + ph.lastUsedDate = phModel.lastUsedDate; + + await this.encryptObjProperty( + phModel, + ph, + { + password: null, + }, + key + ); + + return ph; + } + + async get(id: string): Promise { + const ciphers = await this.stateService.getEncryptedCiphers(); + // eslint-disable-next-line + if (ciphers == null || !ciphers.hasOwnProperty(id)) { + return null; + } + + const localData = await this.stateService.getLocalData(); + return new Cipher(ciphers[id], localData ? localData[id] : null); + } + + async getAll(): Promise { + const localData = await this.stateService.getLocalData(); + const ciphers = await this.stateService.getEncryptedCiphers(); + const response: Cipher[] = []; + for (const id in ciphers) { + // eslint-disable-next-line + if (ciphers.hasOwnProperty(id)) { + response.push(new Cipher(ciphers[id], localData ? localData[id] : null)); + } + } + return response; + } + + @sequentialize(() => "getAllDecrypted") + async getAllDecrypted(): Promise { + const userId = await this.stateService.getUserId(); + if ((await this.getDecryptedCipherCache()) != null) { + if ( + this.searchService != null && + (this.searchService().indexedEntityId ?? userId) !== userId + ) { + await this.searchService().indexCiphers(userId, await this.getDecryptedCipherCache()); + } + return await this.getDecryptedCipherCache(); + } + + const decCiphers: CipherView[] = []; + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + throw new Error("No key."); + } + + const promises: any[] = []; + const ciphers = await this.getAll(); + ciphers.forEach(async (cipher) => { + promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); + }); + + await Promise.all(promises); + decCiphers.sort(this.getLocaleSortingFunction()); + await this.setDecryptedCipherCache(decCiphers); + return decCiphers; + } + + async getAllDecryptedForGrouping(groupingId: string, folder = true): Promise { + const ciphers = await this.getAllDecrypted(); + + return ciphers.filter((cipher) => { + if (cipher.isDeleted) { + return false; + } + if (folder && cipher.folderId === groupingId) { + return true; + } else if ( + !folder && + cipher.collectionIds != null && + cipher.collectionIds.indexOf(groupingId) > -1 + ) { + return true; + } + + return false; + }); + } + + async getAllDecryptedForUrl( + url: string, + includeOtherTypes?: CipherType[], + defaultMatch: UriMatchType = null + ): Promise { + if (url == null && includeOtherTypes == null) { + return Promise.resolve([]); + } + + const domain = Utils.getDomain(url); + const eqDomainsPromise = + domain == null + ? Promise.resolve([]) + : this.settingsService.getEquivalentDomains().then((eqDomains: any[][]) => { + let matches: any[] = []; + eqDomains.forEach((eqDomain) => { + if (eqDomain.length && eqDomain.indexOf(domain) >= 0) { + matches = matches.concat(eqDomain); + } + }); + + if (!matches.length) { + matches.push(domain); + } + + return matches; + }); + + const result = await Promise.all([eqDomainsPromise, this.getAllDecrypted()]); + const matchingDomains = result[0]; + const ciphers = result[1]; + + if (defaultMatch == null) { + defaultMatch = await this.stateService.getDefaultUriMatch(); + if (defaultMatch == null) { + defaultMatch = UriMatchType.Domain; + } + } + + return ciphers.filter((cipher) => { + if (cipher.deletedDate != null) { + return false; + } + if (includeOtherTypes != null && includeOtherTypes.indexOf(cipher.type) > -1) { + return true; + } + + if (url != null && cipher.type === CipherType.Login && cipher.login.uris != null) { + for (let i = 0; i < cipher.login.uris.length; i++) { + const u = cipher.login.uris[i]; + if (u.uri == null) { + continue; + } + + const match = u.match == null ? defaultMatch : u.match; + switch (match) { + case UriMatchType.Domain: + if (domain != null && u.domain != null && matchingDomains.indexOf(u.domain) > -1) { + if (DomainMatchBlacklist.has(u.domain)) { + const domainUrlHost = Utils.getHost(url); + if (!DomainMatchBlacklist.get(u.domain).has(domainUrlHost)) { + return true; + } + } else { + return true; + } + } + break; + case UriMatchType.Host: { + const urlHost = Utils.getHost(url); + if (urlHost != null && urlHost === Utils.getHost(u.uri)) { + return true; + } + break; + } + case UriMatchType.Exact: + if (url === u.uri) { + return true; + } + break; + case UriMatchType.StartsWith: + if (url.startsWith(u.uri)) { + return true; + } + break; + case UriMatchType.RegularExpression: + try { + const regex = new RegExp(u.uri, "i"); + if (regex.test(url)) { + return true; + } + } catch (e) { + this.logService.error(e); + } + break; + case UriMatchType.Never: + default: + break; + } + } + } + + return false; + }); + } + + async getAllFromApiForOrganization(organizationId: string): Promise { + const ciphers = await this.apiService.getCiphersOrganization(organizationId); + if (ciphers != null && ciphers.data != null && ciphers.data.length) { + const decCiphers: CipherView[] = []; + const promises: any[] = []; + ciphers.data.forEach((r) => { + const data = new CipherData(r); + const cipher = new Cipher(data); + promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); + }); + await Promise.all(promises); + decCiphers.sort(this.getLocaleSortingFunction()); + return decCiphers; + } else { + return []; + } + } + + async getLastUsedForUrl(url: string, autofillOnPageLoad = false): Promise { + return this.getCipherForUrl(url, true, false, autofillOnPageLoad); + } + + async getLastLaunchedForUrl(url: string, autofillOnPageLoad = false): Promise { + return this.getCipherForUrl(url, false, true, autofillOnPageLoad); + } + + async getNextCipherForUrl(url: string): Promise { + return this.getCipherForUrl(url, false, false, false); + } + + updateLastUsedIndexForUrl(url: string) { + this.sortedCiphersCache.updateLastUsedIndex(url); + } + + async updateLastUsedDate(id: string): Promise { + let ciphersLocalData = await this.stateService.getLocalData(); + if (!ciphersLocalData) { + ciphersLocalData = {}; + } + + if (ciphersLocalData[id]) { + ciphersLocalData[id].lastUsedDate = new Date().getTime(); + } else { + ciphersLocalData[id] = { + lastUsedDate: new Date().getTime(), + }; + } + + await this.stateService.setLocalData(ciphersLocalData); + + const decryptedCipherCache = await this.stateService.getDecryptedCiphers(); + if (!decryptedCipherCache) { + return; + } + + for (let i = 0; i < decryptedCipherCache.length; i++) { + const cached = decryptedCipherCache[i]; + if (cached.id === id) { + cached.localData = ciphersLocalData[id]; + break; + } + } + await this.stateService.setDecryptedCiphers(decryptedCipherCache); + } + + async updateLastLaunchedDate(id: string): Promise { + let ciphersLocalData = await this.stateService.getLocalData(); + if (!ciphersLocalData) { + ciphersLocalData = {}; + } + + if (ciphersLocalData[id]) { + ciphersLocalData[id].lastLaunched = new Date().getTime(); + } else { + ciphersLocalData[id] = { + lastUsedDate: new Date().getTime(), + }; + } + + await this.stateService.setLocalData(ciphersLocalData); + + const decryptedCipherCache = await this.stateService.getDecryptedCiphers(); + if (!decryptedCipherCache) { + return; + } + + for (let i = 0; i < decryptedCipherCache.length; i++) { + const cached = decryptedCipherCache[i]; + if (cached.id === id) { + cached.localData = ciphersLocalData[id]; + break; + } + } + await this.stateService.setDecryptedCiphers(decryptedCipherCache); + } + + async saveNeverDomain(domain: string): Promise { + if (domain == null) { + return; + } + + let domains = await this.stateService.getNeverDomains(); + if (!domains) { + domains = {}; + } + domains[domain] = null; + await this.stateService.setNeverDomains(domains); + } + + async saveWithServer(cipher: Cipher): Promise { + let response: CipherResponse; + if (cipher.id == null) { + if (cipher.collectionIds != null) { + const request = new CipherCreateRequest(cipher); + response = await this.apiService.postCipherCreate(request); + } else { + const request = new CipherRequest(cipher); + response = await this.apiService.postCipher(request); + } + cipher.id = response.id; + } else { + const request = new CipherRequest(cipher); + response = await this.apiService.putCipher(cipher.id, request); + } + + const data = new CipherData( + response, + await this.stateService.getUserId(), + cipher.collectionIds + ); + await this.upsert(data); + } + + async shareWithServer( + cipher: CipherView, + organizationId: string, + collectionIds: string[] + ): Promise { + const attachmentPromises: Promise[] = []; + if (cipher.attachments != null) { + cipher.attachments.forEach((attachment) => { + if (attachment.key == null) { + attachmentPromises.push( + this.shareAttachmentWithServer(attachment, cipher.id, organizationId) + ); + } + }); + } + await Promise.all(attachmentPromises); + + cipher.organizationId = organizationId; + cipher.collectionIds = collectionIds; + const encCipher = await this.encrypt(cipher); + const request = new CipherShareRequest(encCipher); + const response = await this.apiService.putShareCipher(cipher.id, request); + const data = new CipherData(response, await this.stateService.getUserId(), collectionIds); + await this.upsert(data); + } + + async shareManyWithServer( + ciphers: CipherView[], + organizationId: string, + collectionIds: string[] + ): Promise { + const promises: Promise[] = []; + const encCiphers: Cipher[] = []; + for (const cipher of ciphers) { + cipher.organizationId = organizationId; + cipher.collectionIds = collectionIds; + promises.push( + this.encrypt(cipher).then((c) => { + encCiphers.push(c); + }) + ); + } + await Promise.all(promises); + const request = new CipherBulkShareRequest(encCiphers, collectionIds); + try { + await this.apiService.putShareCiphers(request); + } catch (e) { + for (const cipher of ciphers) { + cipher.organizationId = null; + cipher.collectionIds = null; + } + throw e; + } + const userId = await this.stateService.getUserId(); + await this.upsert(encCiphers.map((c) => c.toCipherData(userId))); + } + + saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any, admin = false): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsArrayBuffer(unencryptedFile); + reader.onload = async (evt: any) => { + try { + const cData = await this.saveAttachmentRawWithServer( + cipher, + unencryptedFile.name, + evt.target.result, + admin + ); + resolve(cData); + } catch (e) { + reject(e); + } + }; + reader.onerror = () => { + reject("Error reading file."); + }; + }); + } + + async saveAttachmentRawWithServer( + cipher: Cipher, + filename: string, + data: ArrayBuffer, + admin = false + ): Promise { + const key = await this.cryptoService.getOrgKey(cipher.organizationId); + const encFileName = await this.cryptoService.encrypt(filename, key); + + const dataEncKey = await this.cryptoService.makeEncKey(key); + const encData = await this.cryptoService.encryptToBytes(data, dataEncKey[0]); + + const request: AttachmentRequest = { + key: dataEncKey[1].encryptedString, + fileName: encFileName.encryptedString, + fileSize: encData.buffer.byteLength, + adminRequest: admin, + }; + + let response: CipherResponse; + try { + const uploadDataResponse = await this.apiService.postCipherAttachment(cipher.id, request); + response = admin ? uploadDataResponse.cipherMiniResponse : uploadDataResponse.cipherResponse; + await this.fileUploadService.uploadCipherAttachment( + admin, + uploadDataResponse, + encFileName, + encData + ); + } catch (e) { + if ( + (e instanceof ErrorResponse && (e as ErrorResponse).statusCode === 404) || + (e as ErrorResponse).statusCode === 405 + ) { + response = await this.legacyServerAttachmentFileUpload( + admin, + cipher.id, + encFileName, + encData, + dataEncKey[1] + ); + } else if (e instanceof ErrorResponse) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } else { + throw e; + } + } + + const cData = new CipherData( + response, + await this.stateService.getUserId(), + cipher.collectionIds + ); + if (!admin) { + await this.upsert(cData); + } + return new Cipher(cData); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async legacyServerAttachmentFileUpload( + admin: boolean, + cipherId: string, + encFileName: EncString, + encData: EncArrayBuffer, + key: EncString + ) { + const fd = new FormData(); + try { + const blob = new Blob([encData.buffer], { type: "application/octet-stream" }); + fd.append("key", key.encryptedString); + fd.append("data", blob, encFileName.encryptedString); + } catch (e) { + if (Utils.isNode && !Utils.isBrowser) { + fd.append("key", key.encryptedString); + fd.append( + "data", + Buffer.from(encData.buffer) as any, + { + filepath: encFileName.encryptedString, + contentType: "application/octet-stream", + } as any + ); + } else { + throw e; + } + } + + let response: CipherResponse; + try { + if (admin) { + response = await this.apiService.postCipherAttachmentAdminLegacy(cipherId, fd); + } else { + response = await this.apiService.postCipherAttachmentLegacy(cipherId, fd); + } + } catch (e) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } + + return response; + } + + async saveCollectionsWithServer(cipher: Cipher): Promise { + const request = new CipherCollectionsRequest(cipher.collectionIds); + await this.apiService.putCipherCollections(cipher.id, request); + const data = cipher.toCipherData(await this.stateService.getUserId()); + await this.upsert(data); + } + + async upsert(cipher: CipherData | CipherData[]): Promise { + let ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + ciphers = {}; + } + + if (cipher instanceof CipherData) { + const c = cipher as CipherData; + ciphers[c.id] = c; + } else { + (cipher as CipherData[]).forEach((c) => { + ciphers[c.id] = c; + }); + } + + await this.replace(ciphers); + } + + async replace(ciphers: { [id: string]: CipherData }): Promise { + await this.clearDecryptedCiphersState(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async clear(userId?: string): Promise { + await this.clearEncryptedCiphersState(userId); + await this.clearCache(userId); + } + + async moveManyWithServer(ids: string[], folderId: string): Promise { + await this.apiService.putMoveCiphers(new CipherBulkMoveRequest(ids, folderId)); + + let ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + ciphers = {}; + } + + ids.forEach((id) => { + // eslint-disable-next-line + if (ciphers.hasOwnProperty(id)) { + ciphers[id].folderId = folderId; + } + }); + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async delete(id: string | string[]): Promise { + const ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + return; + } + + if (typeof id === "string") { + if (ciphers[id] == null) { + return; + } + delete ciphers[id]; + } else { + (id as string[]).forEach((i) => { + delete ciphers[i]; + }); + } + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async deleteWithServer(id: string): Promise { + await this.apiService.deleteCipher(id); + await this.delete(id); + } + + async deleteManyWithServer(ids: string[]): Promise { + await this.apiService.deleteManyCiphers(new CipherBulkDeleteRequest(ids)); + await this.delete(ids); + } + + async deleteAttachment(id: string, attachmentId: string): Promise { + const ciphers = await this.stateService.getEncryptedCiphers(); + + // eslint-disable-next-line + if (ciphers == null || !ciphers.hasOwnProperty(id) || ciphers[id].attachments == null) { + return; + } + + for (let i = 0; i < ciphers[id].attachments.length; i++) { + if (ciphers[id].attachments[i].id === attachmentId) { + ciphers[id].attachments.splice(i, 1); + } + } + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async deleteAttachmentWithServer(id: string, attachmentId: string): Promise { + try { + await this.apiService.deleteCipherAttachment(id, attachmentId); + } catch (e) { + return Promise.reject((e as ErrorResponse).getSingleMessage()); + } + await this.deleteAttachment(id, attachmentId); + } + + sortCiphersByLastUsed(a: CipherView, b: CipherView): number { + const aLastUsed = + a.localData && a.localData.lastUsedDate ? (a.localData.lastUsedDate as number) : null; + const bLastUsed = + b.localData && b.localData.lastUsedDate ? (b.localData.lastUsedDate as number) : null; + + const bothNotNull = aLastUsed != null && bLastUsed != null; + if (bothNotNull && aLastUsed < bLastUsed) { + return 1; + } + if (aLastUsed != null && bLastUsed == null) { + return -1; + } + + if (bothNotNull && aLastUsed > bLastUsed) { + return -1; + } + if (bLastUsed != null && aLastUsed == null) { + return 1; + } + + return 0; + } + + sortCiphersByLastUsedThenName(a: CipherView, b: CipherView): number { + const result = this.sortCiphersByLastUsed(a, b); + if (result !== 0) { + return result; + } + + return this.getLocaleSortingFunction()(a, b); + } + + getLocaleSortingFunction(): (a: CipherView, b: CipherView) => number { + return (a, b) => { + let aName = a.name; + let bName = b.name; + + if (aName == null && bName != null) { + return -1; + } + if (aName != null && bName == null) { + return 1; + } + if (aName == null && bName == null) { + return 0; + } + + const result = this.i18nService.collator + ? this.i18nService.collator.compare(aName, bName) + : aName.localeCompare(bName); + + if (result !== 0 || a.type !== CipherType.Login || b.type !== CipherType.Login) { + return result; + } + + if (a.login.username != null) { + aName += a.login.username; + } + + if (b.login.username != null) { + bName += b.login.username; + } + + return this.i18nService.collator + ? this.i18nService.collator.compare(aName, bName) + : aName.localeCompare(bName); + }; + } + + async softDelete(id: string | string[]): Promise { + const ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + return; + } + + const setDeletedDate = (cipherId: string) => { + if (ciphers[cipherId] == null) { + return; + } + ciphers[cipherId].deletedDate = new Date().toISOString(); + }; + + if (typeof id === "string") { + setDeletedDate(id); + } else { + (id as string[]).forEach(setDeletedDate); + } + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async softDeleteWithServer(id: string): Promise { + await this.apiService.putDeleteCipher(id); + await this.softDelete(id); + } + + async softDeleteManyWithServer(ids: string[]): Promise { + await this.apiService.putDeleteManyCiphers(new CipherBulkDeleteRequest(ids)); + await this.softDelete(ids); + } + + async restore( + cipher: { id: string; revisionDate: string } | { id: string; revisionDate: string }[] + ) { + const ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + return; + } + + const clearDeletedDate = (c: { id: string; revisionDate: string }) => { + if (ciphers[c.id] == null) { + return; + } + ciphers[c.id].deletedDate = null; + ciphers[c.id].revisionDate = c.revisionDate; + }; + + if (cipher.constructor.name === Array.name) { + (cipher as { id: string; revisionDate: string }[]).forEach(clearDeletedDate); + } else { + clearDeletedDate(cipher as { id: string; revisionDate: string }); + } + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async restoreWithServer(id: string): Promise { + const response = await this.apiService.putRestoreCipher(id); + await this.restore({ id: id, revisionDate: response.revisionDate }); + } + + async restoreManyWithServer(ids: string[]): Promise { + const response = await this.apiService.putRestoreManyCiphers(new CipherBulkRestoreRequest(ids)); + const restores: { id: string; revisionDate: string }[] = []; + for (const cipher of response.data) { + restores.push({ id: cipher.id, revisionDate: cipher.revisionDate }); + } + await this.restore(restores); + } + + // Helpers + + private async shareAttachmentWithServer( + attachmentView: AttachmentView, + cipherId: string, + organizationId: string + ): Promise { + const attachmentResponse = await this.apiService.nativeFetch( + new Request(attachmentView.url, { cache: "no-store" }) + ); + if (attachmentResponse.status !== 200) { + throw Error("Failed to download attachment: " + attachmentResponse.status.toString()); + } + + const buf = await attachmentResponse.arrayBuffer(); + const decBuf = await this.cryptoService.decryptFromBytes(buf, null); + const key = await this.cryptoService.getOrgKey(organizationId); + const encFileName = await this.cryptoService.encrypt(attachmentView.fileName, key); + + const dataEncKey = await this.cryptoService.makeEncKey(key); + const encData = await this.cryptoService.encryptToBytes(decBuf, dataEncKey[0]); + + const fd = new FormData(); + try { + const blob = new Blob([encData.buffer], { type: "application/octet-stream" }); + fd.append("key", dataEncKey[1].encryptedString); + fd.append("data", blob, encFileName.encryptedString); + } catch (e) { + if (Utils.isNode && !Utils.isBrowser) { + fd.append("key", dataEncKey[1].encryptedString); + fd.append( + "data", + Buffer.from(encData.buffer) as any, + { + filepath: encFileName.encryptedString, + contentType: "application/octet-stream", + } as any + ); + } else { + throw e; + } + } + + try { + await this.apiService.postShareCipherAttachment( + cipherId, + attachmentView.id, + fd, + organizationId + ); + } catch (e) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } + } + + private async encryptObjProperty( + model: V, + obj: D, + map: any, + key: SymmetricCryptoKey + ): Promise { + const promises = []; + const self = this; + + for (const prop in map) { + // eslint-disable-next-line + if (!map.hasOwnProperty(prop)) { + continue; + } + + (function (theProp, theObj) { + const p = Promise.resolve() + .then(() => { + const modelProp = (model as any)[map[theProp] || theProp]; + if (modelProp && modelProp !== "") { + return self.cryptoService.encrypt(modelProp, key); + } + return null; + }) + .then((val: EncString) => { + (theObj as any)[theProp] = val; + }); + promises.push(p); + })(prop, obj); + } + + await Promise.all(promises); + } + + private async encryptCipherData(cipher: Cipher, model: CipherView, key: SymmetricCryptoKey) { + switch (cipher.type) { + case CipherType.Login: + cipher.login = new Login(); + cipher.login.passwordRevisionDate = model.login.passwordRevisionDate; + cipher.login.autofillOnPageLoad = model.login.autofillOnPageLoad; + await this.encryptObjProperty( + model.login, + cipher.login, + { + username: null, + password: null, + totp: null, + }, + key + ); + + if (model.login.uris != null) { + cipher.login.uris = []; + for (let i = 0; i < model.login.uris.length; i++) { + const loginUri = new LoginUri(); + loginUri.match = model.login.uris[i].match; + await this.encryptObjProperty( + model.login.uris[i], + loginUri, + { + uri: null, + }, + key + ); + cipher.login.uris.push(loginUri); + } + } + return; + case CipherType.SecureNote: + cipher.secureNote = new SecureNote(); + cipher.secureNote.type = model.secureNote.type; + return; + case CipherType.Card: + cipher.card = new Card(); + await this.encryptObjProperty( + model.card, + cipher.card, + { + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }, + key + ); + return; + case CipherType.Identity: + cipher.identity = new Identity(); + await this.encryptObjProperty( + model.identity, + cipher.identity, + { + title: null, + firstName: null, + middleName: null, + lastName: null, + address1: null, + address2: null, + address3: null, + city: null, + state: null, + postalCode: null, + country: null, + company: null, + email: null, + phone: null, + ssn: null, + username: null, + passportNumber: null, + licenseNumber: null, + }, + key + ); + return; + default: + throw new Error("Unknown cipher type."); + } + } + + private async getCipherForUrl( + url: string, + lastUsed: boolean, + lastLaunched: boolean, + autofillOnPageLoad: boolean + ): Promise { + const cacheKey = autofillOnPageLoad ? "autofillOnPageLoad-" + url : url; + + if (!this.sortedCiphersCache.isCached(cacheKey)) { + let ciphers = await this.getAllDecryptedForUrl(url); + if (!ciphers) { + return null; + } + + if (autofillOnPageLoad) { + const autofillOnPageLoadDefault = await this.stateService.getAutoFillOnPageLoadDefault(); + ciphers = ciphers.filter( + (cipher) => + cipher.login.autofillOnPageLoad || + (cipher.login.autofillOnPageLoad == null && autofillOnPageLoadDefault !== false) + ); + if (ciphers.length === 0) { + return null; + } + } + + this.sortedCiphersCache.addCiphers(cacheKey, ciphers); + } + + if (lastLaunched) { + return this.sortedCiphersCache.getLastLaunched(cacheKey); + } else if (lastUsed) { + return this.sortedCiphersCache.getLastUsed(cacheKey); + } else { + return this.sortedCiphersCache.getNext(cacheKey); + } + } + + private async clearEncryptedCiphersState(userId?: string) { + await this.stateService.setEncryptedCiphers(null, { userId: userId }); + } + + private async clearDecryptedCiphersState(userId?: string) { + await this.stateService.setDecryptedCiphers(null, { userId: userId }); + this.clearSortedCiphers(); + } + + private clearSortedCiphers() { + this.sortedCiphersCache.clear(); + } +} diff --git a/jslib/common/src/services/collection.service.ts b/jslib/common/src/services/collection.service.ts new file mode 100644 index 00000000..4d3563d7 --- /dev/null +++ b/jslib/common/src/services/collection.service.ts @@ -0,0 +1,157 @@ +import { CollectionService as CollectionServiceAbstraction } from "../abstractions/collection.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { StateService } from "../abstractions/state.service"; +import { ServiceUtils } from "../misc/serviceUtils"; +import { Utils } from "../misc/utils"; +import { CollectionData } from "../models/data/collectionData"; +import { Collection } from "../models/domain/collection"; +import { TreeNode } from "../models/domain/treeNode"; +import { CollectionView } from "../models/view/collectionView"; + +const NestingDelimiter = "/"; + +export class CollectionService implements CollectionServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private i18nService: I18nService, + private stateService: StateService + ) {} + + async clearCache(userId?: string): Promise { + await this.stateService.setDecryptedCollections(null, { userId: userId }); + } + + async encrypt(model: CollectionView): Promise { + if (model.organizationId == null) { + throw new Error("Collection has no organization id."); + } + const key = await this.cryptoService.getOrgKey(model.organizationId); + if (key == null) { + throw new Error("No key for this collection's organization."); + } + const collection = new Collection(); + collection.id = model.id; + collection.organizationId = model.organizationId; + collection.readOnly = model.readOnly; + collection.name = await this.cryptoService.encrypt(model.name, key); + return collection; + } + + async decryptMany(collections: Collection[]): Promise { + if (collections == null) { + return []; + } + const decCollections: CollectionView[] = []; + const promises: Promise[] = []; + collections.forEach((collection) => { + promises.push(collection.decrypt().then((c) => decCollections.push(c))); + }); + await Promise.all(promises); + return decCollections.sort(Utils.getSortFunction(this.i18nService, "name")); + } + + async get(id: string): Promise { + const collections = await this.stateService.getEncryptedCollections(); + // eslint-disable-next-line + if (collections == null || !collections.hasOwnProperty(id)) { + return null; + } + + return new Collection(collections[id]); + } + + async getAll(): Promise { + const collections = await this.stateService.getEncryptedCollections(); + const response: Collection[] = []; + for (const id in collections) { + // eslint-disable-next-line + if (collections.hasOwnProperty(id)) { + response.push(new Collection(collections[id])); + } + } + return response; + } + + async getAllDecrypted(): Promise { + let decryptedCollections = await this.stateService.getDecryptedCollections(); + if (decryptedCollections != null) { + return decryptedCollections; + } + + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + throw new Error("No key."); + } + + const collections = await this.getAll(); + decryptedCollections = await this.decryptMany(collections); + await this.stateService.setDecryptedCollections(decryptedCollections); + return decryptedCollections; + } + + async getAllNested(collections: CollectionView[] = null): Promise[]> { + if (collections == null) { + collections = await this.getAllDecrypted(); + } + const nodes: TreeNode[] = []; + collections.forEach((c) => { + const collectionCopy = new CollectionView(); + collectionCopy.id = c.id; + collectionCopy.organizationId = c.organizationId; + const parts = c.name != null ? c.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : []; + ServiceUtils.nestedTraverse(nodes, 0, parts, collectionCopy, null, NestingDelimiter); + }); + return nodes; + } + + async getNested(id: string): Promise> { + const collections = await this.getAllNested(); + return ServiceUtils.getTreeNodeObject(collections, id) as TreeNode; + } + + async upsert(collection: CollectionData | CollectionData[]): Promise { + let collections = await this.stateService.getEncryptedCollections(); + if (collections == null) { + collections = {}; + } + + if (collection instanceof CollectionData) { + const c = collection as CollectionData; + collections[c.id] = c; + } else { + (collection as CollectionData[]).forEach((c) => { + collections[c.id] = c; + }); + } + + await this.replace(collections); + } + + async replace(collections: { [id: string]: CollectionData }): Promise { + await this.clearCache(); + await this.stateService.setEncryptedCollections(collections); + } + + async clear(userId?: string): Promise { + await this.clearCache(userId); + await this.stateService.setEncryptedCollections(null, { userId: userId }); + } + + async delete(id: string | string[]): Promise { + const collections = await this.stateService.getEncryptedCollections(); + if (collections == null) { + return; + } + + if (typeof id === "string") { + delete collections[id]; + } else { + (id as string[]).forEach((i) => { + delete collections[i]; + }); + } + + await this.replace(collections); + } +} diff --git a/jslib/common/src/services/consoleLog.service.ts b/jslib/common/src/services/consoleLog.service.ts new file mode 100644 index 00000000..9959ef62 --- /dev/null +++ b/jslib/common/src/services/consoleLog.service.ts @@ -0,0 +1,72 @@ +import * as hrtime from "browser-hrtime"; + +import { LogService as LogServiceAbstraction } from "../abstractions/log.service"; +import { LogLevelType } from "../enums/logLevelType"; + +export class ConsoleLogService implements LogServiceAbstraction { + protected timersMap: Map = new Map(); + + constructor( + protected isDev: boolean, + protected filter: (level: LogLevelType) => boolean = null + ) {} + + debug(message: string) { + if (!this.isDev) { + return; + } + this.write(LogLevelType.Debug, message); + } + + info(message: string) { + this.write(LogLevelType.Info, message); + } + + warning(message: string) { + this.write(LogLevelType.Warning, message); + } + + error(message: string) { + this.write(LogLevelType.Error, message); + } + + write(level: LogLevelType, message: string) { + if (this.filter != null && this.filter(level)) { + return; + } + + switch (level) { + case LogLevelType.Debug: + // eslint-disable-next-line + console.log(message); + break; + case LogLevelType.Info: + // eslint-disable-next-line + console.log(message); + break; + case LogLevelType.Warning: + // eslint-disable-next-line + console.warn(message); + break; + case LogLevelType.Error: + // eslint-disable-next-line + console.error(message); + break; + default: + break; + } + } + + time(label = "default") { + if (!this.timersMap.has(label)) { + this.timersMap.set(label, hrtime()); + } + } + + timeEnd(label = "default"): [number, number] { + const elapsed = hrtime(this.timersMap.get(label)); + this.timersMap.delete(label); + this.write(LogLevelType.Info, `${label}: ${elapsed[0] * 1000 + elapsed[1] / 10e6}ms`); + return elapsed; + } +} diff --git a/jslib/common/src/services/container.service.ts b/jslib/common/src/services/container.service.ts new file mode 100644 index 00000000..2880e7c7 --- /dev/null +++ b/jslib/common/src/services/container.service.ts @@ -0,0 +1,20 @@ +import { CryptoService } from "../abstractions/crypto.service"; + +export class ContainerService { + constructor(private cryptoService: CryptoService) {} + + // deprecated, use attachToGlobal instead + attachToWindow(win: any) { + this.attachToGlobal(win); + } + + attachToGlobal(global: any) { + if (!global.bitwardenContainerService) { + global.bitwardenContainerService = this; + } + } + + getCryptoService(): CryptoService { + return this.cryptoService; + } +} diff --git a/jslib/common/src/services/crypto.service.ts b/jslib/common/src/services/crypto.service.ts new file mode 100644 index 00000000..c49d7763 --- /dev/null +++ b/jslib/common/src/services/crypto.service.ts @@ -0,0 +1,969 @@ +import * as bigInt from "big-integer"; + +import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { LogService } from "../abstractions/log.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { StateService } from "../abstractions/state.service"; +import { EncryptionType } from "../enums/encryptionType"; +import { HashPurpose } from "../enums/hashPurpose"; +import { KdfType } from "../enums/kdfType"; +import { KeySuffixOptions } from "../enums/keySuffixOptions"; +import { sequentialize } from "../misc/sequentialize"; +import { Utils } from "../misc/utils"; +import { EEFLongWordList } from "../misc/wordlist"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { EncryptedObject } from "../models/domain/encryptedObject"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { ProfileOrganizationResponse } from "../models/response/profileOrganizationResponse"; +import { ProfileProviderOrganizationResponse } from "../models/response/profileProviderOrganizationResponse"; +import { ProfileProviderResponse } from "../models/response/profileProviderResponse"; + +export class CryptoService implements CryptoServiceAbstraction { + constructor( + private cryptoFunctionService: CryptoFunctionService, + protected platformUtilService: PlatformUtilsService, + protected logService: LogService, + protected stateService: StateService + ) {} + + async setKey(key: SymmetricCryptoKey, userId?: string): Promise { + await this.stateService.setCryptoMasterKey(key, { userId: userId }); + await this.storeKey(key, userId); + } + + async setKeyHash(keyHash: string): Promise { + await this.stateService.setKeyHash(keyHash); + } + + async setEncKey(encKey: string): Promise { + if (encKey == null) { + return; + } + + await this.stateService.setDecryptedCryptoSymmetricKey(null); + await this.stateService.setEncryptedCryptoSymmetricKey(encKey); + } + + async setEncPrivateKey(encPrivateKey: string): Promise { + if (encPrivateKey == null) { + return; + } + + await this.stateService.setDecryptedPrivateKey(null); + await this.stateService.setEncryptedPrivateKey(encPrivateKey); + } + + async setOrgKeys( + orgs: ProfileOrganizationResponse[], + providerOrgs: ProfileProviderOrganizationResponse[] + ): Promise { + const orgKeys: any = {}; + orgs.forEach((org) => { + orgKeys[org.id] = org.key; + }); + + for (const providerOrg of providerOrgs) { + // Convert provider encrypted keys to user encrypted. + const providerKey = await this.getProviderKey(providerOrg.providerId); + const decValue = await this.decryptToBytes(new EncString(providerOrg.key), providerKey); + orgKeys[providerOrg.id] = (await this.rsaEncrypt(decValue)).encryptedString; + } + + await this.stateService.setDecryptedOrganizationKeys(null); + return await this.stateService.setEncryptedOrganizationKeys(orgKeys); + } + + async setProviderKeys(providers: ProfileProviderResponse[]): Promise { + const providerKeys: any = {}; + providers.forEach((provider) => { + providerKeys[provider.id] = provider.key; + }); + + await this.stateService.setDecryptedProviderKeys(null); + return await this.stateService.setEncryptedProviderKeys(providerKeys); + } + + async getKey(keySuffix?: KeySuffixOptions, userId?: string): Promise { + const inMemoryKey = await this.stateService.getCryptoMasterKey({ userId: userId }); + + if (inMemoryKey != null) { + return inMemoryKey; + } + + keySuffix ||= KeySuffixOptions.Auto; + const symmetricKey = await this.getKeyFromStorage(keySuffix, userId); + + if (symmetricKey != null) { + // TODO: Refactor here so get key doesn't also set key + this.setKey(symmetricKey, userId); + } + + return symmetricKey; + } + + async getKeyFromStorage( + keySuffix: KeySuffixOptions, + userId?: string + ): Promise { + const key = await this.retrieveKeyFromStorage(keySuffix, userId); + if (key != null) { + const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key).buffer); + + if (!(await this.validateKey(symmetricKey))) { + this.logService.warning("Wrong key, throwing away stored key"); + await this.clearSecretKeyStore(userId); + return null; + } + + return symmetricKey; + } + return null; + } + + async getKeyHash(): Promise { + return await this.stateService.getKeyHash(); + } + + async compareAndUpdateKeyHash(masterPassword: string, key: SymmetricCryptoKey): Promise { + const storedKeyHash = await this.getKeyHash(); + if (masterPassword != null && storedKeyHash != null) { + const localKeyHash = await this.hashPassword( + masterPassword, + key, + HashPurpose.LocalAuthorization + ); + if (localKeyHash != null && storedKeyHash === localKeyHash) { + return true; + } + + // TODO: remove serverKeyHash check in 1-2 releases after everyone's keyHash has been updated + const serverKeyHash = await this.hashPassword( + masterPassword, + key, + HashPurpose.ServerAuthorization + ); + if (serverKeyHash != null && storedKeyHash === serverKeyHash) { + await this.setKeyHash(localKeyHash); + return true; + } + } + + return false; + } + + @sequentialize(() => "getEncKey") + getEncKey(key: SymmetricCryptoKey = null): Promise { + return this.getEncKeyHelper(key); + } + + async getPublicKey(): Promise { + const inMemoryPublicKey = await this.stateService.getPublicKey(); + if (inMemoryPublicKey != null) { + return inMemoryPublicKey; + } + + const privateKey = await this.getPrivateKey(); + if (privateKey == null) { + return null; + } + + const publicKey = await this.cryptoFunctionService.rsaExtractPublicKey(privateKey); + await this.stateService.setPublicKey(publicKey); + return publicKey; + } + + async getPrivateKey(): Promise { + const decryptedPrivateKey = await this.stateService.getDecryptedPrivateKey(); + if (decryptedPrivateKey != null) { + return decryptedPrivateKey; + } + + const encPrivateKey = await this.stateService.getEncryptedPrivateKey(); + if (encPrivateKey == null) { + return null; + } + + const privateKey = await this.decryptToBytes(new EncString(encPrivateKey), null); + await this.stateService.setDecryptedPrivateKey(privateKey); + return privateKey; + } + + async getFingerprint(userId: string, publicKey?: ArrayBuffer): Promise { + if (publicKey == null) { + publicKey = await this.getPublicKey(); + } + if (publicKey === null) { + throw new Error("No public key available."); + } + const keyFingerprint = await this.cryptoFunctionService.hash(publicKey, "sha256"); + const userFingerprint = await this.cryptoFunctionService.hkdfExpand( + keyFingerprint, + userId, + 32, + "sha256" + ); + return this.hashPhrase(userFingerprint); + } + + @sequentialize(() => "getOrgKeys") + async getOrgKeys(): Promise> { + const orgKeys: Map = new Map(); + const decryptedOrganizationKeys = await this.stateService.getDecryptedOrganizationKeys(); + if (decryptedOrganizationKeys != null && decryptedOrganizationKeys.size > 0) { + return decryptedOrganizationKeys; + } + + const encOrgKeys = await this.stateService.getEncryptedOrganizationKeys(); + if (encOrgKeys == null) { + return null; + } + + let setKey = false; + + for (const orgId in encOrgKeys) { + // eslint-disable-next-line + if (!encOrgKeys.hasOwnProperty(orgId)) { + continue; + } + + const decValue = await this.rsaDecrypt(encOrgKeys[orgId]); + orgKeys.set(orgId, new SymmetricCryptoKey(decValue)); + setKey = true; + } + + if (setKey) { + await this.stateService.setDecryptedOrganizationKeys(orgKeys); + } + + return orgKeys; + } + + async getOrgKey(orgId: string): Promise { + if (orgId == null) { + return null; + } + + const orgKeys = await this.getOrgKeys(); + if (orgKeys == null || !orgKeys.has(orgId)) { + return null; + } + + return orgKeys.get(orgId); + } + + @sequentialize(() => "getProviderKeys") + async getProviderKeys(): Promise> { + const providerKeys: Map = new Map(); + const decryptedProviderKeys = await this.stateService.getDecryptedProviderKeys(); + if (decryptedProviderKeys != null && decryptedProviderKeys.size > 0) { + return decryptedProviderKeys; + } + + const encProviderKeys = await this.stateService.getEncryptedProviderKeys(); + if (encProviderKeys == null) { + return null; + } + + let setKey = false; + + for (const orgId in encProviderKeys) { + // eslint-disable-next-line + if (!encProviderKeys.hasOwnProperty(orgId)) { + continue; + } + + const decValue = await this.rsaDecrypt(encProviderKeys[orgId]); + providerKeys.set(orgId, new SymmetricCryptoKey(decValue)); + setKey = true; + } + + if (setKey) { + await this.stateService.setDecryptedProviderKeys(providerKeys); + } + + return providerKeys; + } + + async getProviderKey(providerId: string): Promise { + if (providerId == null) { + return null; + } + + const providerKeys = await this.getProviderKeys(); + if (providerKeys == null || !providerKeys.has(providerId)) { + return null; + } + + return providerKeys.get(providerId); + } + + async hasKey(): Promise { + return ( + (await this.hasKeyInMemory()) || + (await this.hasKeyStored(KeySuffixOptions.Auto)) || + (await this.hasKeyStored(KeySuffixOptions.Biometric)) + ); + } + + async hasKeyInMemory(userId?: string): Promise { + return (await this.stateService.getCryptoMasterKey({ userId: userId })) != null; + } + + async hasKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise { + switch (keySuffix) { + case KeySuffixOptions.Auto: + return (await this.stateService.getCryptoMasterKeyAuto({ userId: userId })) != null; + case KeySuffixOptions.Biometric: + return (await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId })) === true; + default: + return false; + } + } + + async hasEncKey(): Promise { + return (await this.stateService.getEncryptedCryptoSymmetricKey()) != null; + } + + async clearKey(clearSecretStorage = true, userId?: string): Promise { + await this.stateService.setCryptoMasterKey(null, { userId: userId }); + await this.stateService.setLegacyEtmKey(null, { userId: userId }); + if (clearSecretStorage) { + await this.clearSecretKeyStore(userId); + } + } + + async clearStoredKey(keySuffix: KeySuffixOptions) { + keySuffix === KeySuffixOptions.Auto + ? await this.stateService.setCryptoMasterKeyAuto(null) + : await this.stateService.setCryptoMasterKeyBiometric(null); + } + + async clearKeyHash(userId?: string): Promise { + return await this.stateService.setKeyHash(null, { userId: userId }); + } + + async clearEncKey(memoryOnly?: boolean, userId?: string): Promise { + await this.stateService.setDecryptedCryptoSymmetricKey(null, { userId: userId }); + if (!memoryOnly) { + await this.stateService.setEncryptedCryptoSymmetricKey(null, { userId: userId }); + } + } + + async clearKeyPair(memoryOnly?: boolean, userId?: string): Promise { + const keysToClear: Promise[] = [ + this.stateService.setDecryptedPrivateKey(null, { userId: userId }), + this.stateService.setPublicKey(null, { userId: userId }), + ]; + if (!memoryOnly) { + keysToClear.push(this.stateService.setEncryptedPrivateKey(null, { userId: userId })); + } + return Promise.all(keysToClear); + } + + async clearOrgKeys(memoryOnly?: boolean, userId?: string): Promise { + await this.stateService.setDecryptedOrganizationKeys(null, { userId: userId }); + if (!memoryOnly) { + await this.stateService.setEncryptedOrganizationKeys(null, { userId: userId }); + } + } + + async clearProviderKeys(memoryOnly?: boolean, userId?: string): Promise { + await this.stateService.setDecryptedProviderKeys(null, { userId: userId }); + if (!memoryOnly) { + await this.stateService.setEncryptedProviderKeys(null, { userId: userId }); + } + } + + async clearPinProtectedKey(userId?: string): Promise { + return await this.stateService.setEncryptedPinProtected(null, { userId: userId }); + } + + async clearKeys(userId?: string): Promise { + await this.clearKey(true, userId); + await this.clearKeyHash(userId); + await this.clearOrgKeys(false, userId); + await this.clearProviderKeys(false, userId); + await this.clearEncKey(false, userId); + await this.clearKeyPair(false, userId); + await this.clearPinProtectedKey(userId); + } + + async toggleKey(): Promise { + const key = await this.getKey(); + + await this.setKey(key); + } + + async makeKey( + password: string, + salt: string, + kdf: KdfType, + kdfIterations: number + ): Promise { + let key: ArrayBuffer = null; + if (kdf == null || kdf === KdfType.PBKDF2_SHA256) { + if (kdfIterations == null) { + kdfIterations = 5000; + } else if (kdfIterations < 5000) { + throw new Error("PBKDF2 iteration minimum is 5000."); + } + key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfIterations); + } else { + throw new Error("Unknown Kdf."); + } + return new SymmetricCryptoKey(key); + } + + async makeKeyFromPin( + pin: string, + salt: string, + kdf: KdfType, + kdfIterations: number, + protectedKeyCs: EncString = null + ): Promise { + if (protectedKeyCs == null) { + const pinProtectedKey = await this.stateService.getEncryptedPinProtected(); + if (pinProtectedKey == null) { + throw new Error("No PIN protected key found."); + } + protectedKeyCs = new EncString(pinProtectedKey); + } + const pinKey = await this.makePinKey(pin, salt, kdf, kdfIterations); + const decKey = await this.decryptToBytes(protectedKeyCs, pinKey); + return new SymmetricCryptoKey(decKey); + } + + async makeShareKey(): Promise<[EncString, SymmetricCryptoKey]> { + const shareKey = await this.cryptoFunctionService.randomBytes(64); + const publicKey = await this.getPublicKey(); + const encShareKey = await this.rsaEncrypt(shareKey, publicKey); + return [encShareKey, new SymmetricCryptoKey(shareKey)]; + } + + async makeKeyPair(key?: SymmetricCryptoKey): Promise<[string, EncString]> { + const keyPair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); + const publicB64 = Utils.fromBufferToB64(keyPair[0]); + const privateEnc = await this.encrypt(keyPair[1], key); + return [publicB64, privateEnc]; + } + + async makePinKey( + pin: string, + salt: string, + kdf: KdfType, + kdfIterations: number + ): Promise { + const pinKey = await this.makeKey(pin, salt, kdf, kdfIterations); + return await this.stretchKey(pinKey); + } + + async makeSendKey(keyMaterial: ArrayBuffer): Promise { + const sendKey = await this.cryptoFunctionService.hkdf( + keyMaterial, + "bitwarden-send", + "send", + 64, + "sha256" + ); + return new SymmetricCryptoKey(sendKey); + } + + async hashPassword( + password: string, + key: SymmetricCryptoKey, + hashPurpose?: HashPurpose + ): Promise { + if (key == null) { + key = await this.getKey(); + } + if (password == null || key == null) { + throw new Error("Invalid parameters."); + } + + const iterations = hashPurpose === HashPurpose.LocalAuthorization ? 2 : 1; + const hash = await this.cryptoFunctionService.pbkdf2(key.key, password, "sha256", iterations); + return Utils.fromBufferToB64(hash); + } + + async makeEncKey(key: SymmetricCryptoKey): Promise<[SymmetricCryptoKey, EncString]> { + const theKey = await this.getKeyForEncryption(key); + const encKey = await this.cryptoFunctionService.randomBytes(64); + return this.buildEncKey(theKey, encKey); + } + + async remakeEncKey( + key: SymmetricCryptoKey, + encKey?: SymmetricCryptoKey + ): Promise<[SymmetricCryptoKey, EncString]> { + if (encKey == null) { + encKey = await this.getEncKey(); + } + return this.buildEncKey(key, encKey.key); + } + + async encrypt(plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey): Promise { + if (plainValue == null) { + return Promise.resolve(null); + } + + let plainBuf: ArrayBuffer; + if (typeof plainValue === "string") { + plainBuf = Utils.fromUtf8ToArray(plainValue).buffer; + } else { + plainBuf = plainValue; + } + + const encObj = await this.aesEncrypt(plainBuf, key); + const iv = Utils.fromBufferToB64(encObj.iv); + const data = Utils.fromBufferToB64(encObj.data); + const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : null; + return new EncString(encObj.key.encType, data, iv, mac); + } + + async encryptToBytes(plainValue: ArrayBuffer, key?: SymmetricCryptoKey): Promise { + const encValue = await this.aesEncrypt(plainValue, key); + let macLen = 0; + if (encValue.mac != null) { + macLen = encValue.mac.byteLength; + } + + const encBytes = new Uint8Array(1 + encValue.iv.byteLength + macLen + encValue.data.byteLength); + encBytes.set([encValue.key.encType]); + encBytes.set(new Uint8Array(encValue.iv), 1); + if (encValue.mac != null) { + encBytes.set(new Uint8Array(encValue.mac), 1 + encValue.iv.byteLength); + } + + encBytes.set(new Uint8Array(encValue.data), 1 + encValue.iv.byteLength + macLen); + return new EncArrayBuffer(encBytes.buffer); + } + + async rsaEncrypt(data: ArrayBuffer, publicKey?: ArrayBuffer): Promise { + if (publicKey == null) { + publicKey = await this.getPublicKey(); + } + if (publicKey == null) { + throw new Error("Public key unavailable."); + } + + const encBytes = await this.cryptoFunctionService.rsaEncrypt(data, publicKey, "sha1"); + return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(encBytes)); + } + + async rsaDecrypt(encValue: string, privateKeyValue?: ArrayBuffer): Promise { + const headerPieces = encValue.split("."); + let encType: EncryptionType = null; + let encPieces: string[]; + + if (headerPieces.length === 1) { + encType = EncryptionType.Rsa2048_OaepSha256_B64; + encPieces = [headerPieces[0]]; + } else if (headerPieces.length === 2) { + try { + encType = parseInt(headerPieces[0], null); + encPieces = headerPieces[1].split("|"); + } catch (e) { + this.logService.error(e); + } + } + + switch (encType) { + case EncryptionType.Rsa2048_OaepSha256_B64: + case EncryptionType.Rsa2048_OaepSha1_B64: + case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: // HmacSha256 types are deprecated + case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64: + break; + default: + throw new Error("encType unavailable."); + } + + if (encPieces == null || encPieces.length <= 0) { + throw new Error("encPieces unavailable."); + } + + const data = Utils.fromB64ToArray(encPieces[0]).buffer; + const privateKey = privateKeyValue ?? (await this.getPrivateKey()); + if (privateKey == null) { + throw new Error("No private key."); + } + + let alg: "sha1" | "sha256" = "sha1"; + switch (encType) { + case EncryptionType.Rsa2048_OaepSha256_B64: + case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: + alg = "sha256"; + break; + case EncryptionType.Rsa2048_OaepSha1_B64: + case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64: + break; + default: + throw new Error("encType unavailable."); + } + + return this.cryptoFunctionService.rsaDecrypt(data, privateKey, alg); + } + + async decryptToBytes(encString: EncString, key?: SymmetricCryptoKey): Promise { + const iv = Utils.fromB64ToArray(encString.iv).buffer; + const data = Utils.fromB64ToArray(encString.data).buffer; + const mac = encString.mac ? Utils.fromB64ToArray(encString.mac).buffer : null; + const decipher = await this.aesDecryptToBytes(encString.encryptionType, data, iv, mac, key); + if (decipher == null) { + return null; + } + + return decipher; + } + + async decryptToUtf8(encString: EncString, key?: SymmetricCryptoKey): Promise { + return await this.aesDecryptToUtf8( + encString.encryptionType, + encString.data, + encString.iv, + encString.mac, + key + ); + } + + async decryptFromBytes(encBuf: ArrayBuffer, key: SymmetricCryptoKey): Promise { + if (encBuf == null) { + throw new Error("no encBuf."); + } + + const encBytes = new Uint8Array(encBuf); + const encType = encBytes[0]; + let ctBytes: Uint8Array = null; + let ivBytes: Uint8Array = null; + let macBytes: Uint8Array = null; + + switch (encType) { + case EncryptionType.AesCbc128_HmacSha256_B64: + case EncryptionType.AesCbc256_HmacSha256_B64: + if (encBytes.length <= 49) { + // 1 + 16 + 32 + ctLength + return null; + } + + ivBytes = encBytes.slice(1, 17); + macBytes = encBytes.slice(17, 49); + ctBytes = encBytes.slice(49); + break; + case EncryptionType.AesCbc256_B64: + if (encBytes.length <= 17) { + // 1 + 16 + ctLength + return null; + } + + ivBytes = encBytes.slice(1, 17); + ctBytes = encBytes.slice(17); + break; + default: + return null; + } + + return await this.aesDecryptToBytes( + encType, + ctBytes.buffer, + ivBytes.buffer, + macBytes != null ? macBytes.buffer : null, + key + ); + } + + // EFForg/OpenWireless + // ref https://github.com/EFForg/OpenWireless/blob/master/app/js/diceware.js + async randomNumber(min: number, max: number): Promise { + let rval = 0; + const range = max - min + 1; + const bitsNeeded = Math.ceil(Math.log2(range)); + if (bitsNeeded > 53) { + throw new Error("We cannot generate numbers larger than 53 bits."); + } + + const bytesNeeded = Math.ceil(bitsNeeded / 8); + const mask = Math.pow(2, bitsNeeded) - 1; + // 7776 -> (2^13 = 8192) -1 == 8191 or 0x00001111 11111111 + + // Fill a byte array with N random numbers + const byteArray = new Uint8Array(await this.cryptoFunctionService.randomBytes(bytesNeeded)); + + let p = (bytesNeeded - 1) * 8; + for (let i = 0; i < bytesNeeded; i++) { + rval += byteArray[i] * Math.pow(2, p); + p -= 8; + } + + // Use & to apply the mask and reduce the number of recursive lookups + rval = rval & mask; + + if (rval >= range) { + // Integer out of acceptable range + return this.randomNumber(min, max); + } + + // Return an integer that falls within the range + return min + rval; + } + + async validateKey(key: SymmetricCryptoKey) { + try { + const encPrivateKey = await this.stateService.getEncryptedPrivateKey(); + const encKey = await this.getEncKeyHelper(key); + if (encPrivateKey == null || encKey == null) { + return false; + } + + const privateKey = await this.decryptToBytes(new EncString(encPrivateKey), encKey); + await this.cryptoFunctionService.rsaExtractPublicKey(privateKey); + } catch (e) { + return false; + } + + return true; + } + + // Helpers + protected async storeKey(key: SymmetricCryptoKey, userId?: string) { + if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId)) { + await this.stateService.setCryptoMasterKeyAuto(key.keyB64, { userId: userId }); + } else if (await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) { + await this.stateService.setCryptoMasterKeyBiometric(key.keyB64, { userId: userId }); + } else { + await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId }); + await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + } + } + + protected async shouldStoreKey(keySuffix: KeySuffixOptions, userId?: string) { + let shouldStoreKey = false; + if (keySuffix === KeySuffixOptions.Auto) { + const vaultTimeout = await this.stateService.getVaultTimeout({ userId: userId }); + shouldStoreKey = vaultTimeout == null; + } else if (keySuffix === KeySuffixOptions.Biometric) { + const biometricUnlock = await this.stateService.getBiometricUnlock({ userId: userId }); + shouldStoreKey = biometricUnlock && this.platformUtilService.supportsSecureStorage(); + } + return shouldStoreKey; + } + + protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string) { + return keySuffix === KeySuffixOptions.Auto + ? await this.stateService.getCryptoMasterKeyAuto({ userId: userId }) + : await this.stateService.getCryptoMasterKeyBiometric({ userId: userId }); + } + + private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise { + const obj = new EncryptedObject(); + obj.key = await this.getKeyForEncryption(key); + obj.iv = await this.cryptoFunctionService.randomBytes(16); + obj.data = await this.cryptoFunctionService.aesEncrypt(data, obj.iv, obj.key.encKey); + + if (obj.key.macKey != null) { + const macData = new Uint8Array(obj.iv.byteLength + obj.data.byteLength); + macData.set(new Uint8Array(obj.iv), 0); + macData.set(new Uint8Array(obj.data), obj.iv.byteLength); + obj.mac = await this.cryptoFunctionService.hmac(macData.buffer, obj.key.macKey, "sha256"); + } + + return obj; + } + + private async aesDecryptToUtf8( + encType: EncryptionType, + data: string, + iv: string, + mac: string, + key: SymmetricCryptoKey + ): Promise { + const keyForEnc = await this.getKeyForEncryption(key); + const theKey = await this.resolveLegacyKey(encType, keyForEnc); + + if (theKey.macKey != null && mac == null) { + this.logService.error("mac required."); + return null; + } + + if (theKey.encType !== encType) { + this.logService.error("encType unavailable."); + return null; + } + + const fastParams = this.cryptoFunctionService.aesDecryptFastParameters(data, iv, mac, theKey); + if (fastParams.macKey != null && fastParams.mac != null) { + const computedMac = await this.cryptoFunctionService.hmacFast( + fastParams.macData, + fastParams.macKey, + "sha256" + ); + const macsEqual = await this.cryptoFunctionService.compareFast(fastParams.mac, computedMac); + if (!macsEqual) { + this.logService.error("mac failed."); + return null; + } + } + + return this.cryptoFunctionService.aesDecryptFast(fastParams); + } + + private async aesDecryptToBytes( + encType: EncryptionType, + data: ArrayBuffer, + iv: ArrayBuffer, + mac: ArrayBuffer, + key: SymmetricCryptoKey + ): Promise { + const keyForEnc = await this.getKeyForEncryption(key); + const theKey = await this.resolveLegacyKey(encType, keyForEnc); + + if (theKey.macKey != null && mac == null) { + return null; + } + + if (theKey.encType !== encType) { + return null; + } + + if (theKey.macKey != null && mac != null) { + const macData = new Uint8Array(iv.byteLength + data.byteLength); + macData.set(new Uint8Array(iv), 0); + macData.set(new Uint8Array(data), iv.byteLength); + const computedMac = await this.cryptoFunctionService.hmac( + macData.buffer, + theKey.macKey, + "sha256" + ); + if (computedMac === null) { + return null; + } + + const macsMatch = await this.cryptoFunctionService.compare(mac, computedMac); + if (!macsMatch) { + this.logService.error("mac failed."); + return null; + } + } + + return await this.cryptoFunctionService.aesDecrypt(data, iv, theKey.encKey); + } + + private async getKeyForEncryption(key?: SymmetricCryptoKey): Promise { + if (key != null) { + return key; + } + + const encKey = await this.getEncKey(); + if (encKey != null) { + return encKey; + } + + return await this.getKey(); + } + + private async resolveLegacyKey( + encType: EncryptionType, + key: SymmetricCryptoKey + ): Promise { + if ( + encType === EncryptionType.AesCbc128_HmacSha256_B64 && + key.encType === EncryptionType.AesCbc256_B64 + ) { + // Old encrypt-then-mac scheme, make a new key + let legacyKey = await this.stateService.getLegacyEtmKey(); + if (legacyKey == null) { + legacyKey = new SymmetricCryptoKey(key.key, EncryptionType.AesCbc128_HmacSha256_B64); + await this.stateService.setLegacyEtmKey(legacyKey); + } + return legacyKey; + } + + return key; + } + + private async stretchKey(key: SymmetricCryptoKey): Promise { + const newKey = new Uint8Array(64); + const encKey = await this.cryptoFunctionService.hkdfExpand(key.key, "enc", 32, "sha256"); + const macKey = await this.cryptoFunctionService.hkdfExpand(key.key, "mac", 32, "sha256"); + newKey.set(new Uint8Array(encKey)); + newKey.set(new Uint8Array(macKey), 32); + return new SymmetricCryptoKey(newKey.buffer); + } + + private async hashPhrase(hash: ArrayBuffer, minimumEntropy = 64) { + const entropyPerWord = Math.log(EEFLongWordList.length) / Math.log(2); + let numWords = Math.ceil(minimumEntropy / entropyPerWord); + + const hashArr = Array.from(new Uint8Array(hash)); + const entropyAvailable = hashArr.length * 4; + if (numWords * entropyPerWord > entropyAvailable) { + throw new Error("Output entropy of hash function is too small"); + } + + const phrase: string[] = []; + let hashNumber = bigInt.fromArray(hashArr, 256); + while (numWords--) { + const remainder = hashNumber.mod(EEFLongWordList.length); + hashNumber = hashNumber.divide(EEFLongWordList.length); + phrase.push(EEFLongWordList[remainder as any]); + } + return phrase; + } + + private async buildEncKey( + key: SymmetricCryptoKey, + encKey: ArrayBuffer + ): Promise<[SymmetricCryptoKey, EncString]> { + let encKeyEnc: EncString = null; + if (key.key.byteLength === 32) { + const newKey = await this.stretchKey(key); + encKeyEnc = await this.encrypt(encKey, newKey); + } else if (key.key.byteLength === 64) { + encKeyEnc = await this.encrypt(encKey, key); + } else { + throw new Error("Invalid key size."); + } + return [new SymmetricCryptoKey(encKey), encKeyEnc]; + } + + private async clearSecretKeyStore(userId?: string): Promise { + await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId }); + await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + } + + private async getEncKeyHelper(key: SymmetricCryptoKey = null): Promise { + const inMemoryKey = await this.stateService.getDecryptedCryptoSymmetricKey(); + if (inMemoryKey != null) { + return inMemoryKey; + } + + const encKey = await this.stateService.getEncryptedCryptoSymmetricKey(); + if (encKey == null) { + return null; + } + + if (key == null) { + key = await this.getKey(); + } + if (key == null) { + return null; + } + + let decEncKey: ArrayBuffer; + const encKeyCipher = new EncString(encKey); + if (encKeyCipher.encryptionType === EncryptionType.AesCbc256_B64) { + decEncKey = await this.decryptToBytes(encKeyCipher, key); + } else if (encKeyCipher.encryptionType === EncryptionType.AesCbc256_HmacSha256_B64) { + const newKey = await this.stretchKey(key); + decEncKey = await this.decryptToBytes(encKeyCipher, newKey); + } else { + throw new Error("Unsupported encKey type."); + } + if (decEncKey == null) { + return null; + } + const symmetricCryptoKey = new SymmetricCryptoKey(decEncKey); + await this.stateService.setDecryptedCryptoSymmetricKey(symmetricCryptoKey); + return symmetricCryptoKey; + } +} diff --git a/jslib/common/src/services/environment.service.ts b/jslib/common/src/services/environment.service.ts new file mode 100644 index 00000000..b13afd59 --- /dev/null +++ b/jslib/common/src/services/environment.service.ts @@ -0,0 +1,188 @@ +import { Observable, Subject } from "rxjs"; + +import { + EnvironmentService as EnvironmentServiceAbstraction, + Urls, +} from "../abstractions/environment.service"; +import { StateService } from "../abstractions/state.service"; +import { EnvironmentUrls } from "../models/domain/environmentUrls"; + +export class EnvironmentService implements EnvironmentServiceAbstraction { + private readonly urlsSubject = new Subject(); + urls: Observable = this.urlsSubject; + + private baseUrl: string; + private webVaultUrl: string; + private apiUrl: string; + private identityUrl: string; + private iconsUrl: string; + private notificationsUrl: string; + private eventsUrl: string; + private keyConnectorUrl: string; + + constructor(private stateService: StateService) { + this.stateService.activeAccount.subscribe(async () => { + await this.setUrlsFromStorage(); + }); + } + + hasBaseUrl() { + return this.baseUrl != null; + } + + getNotificationsUrl() { + if (this.notificationsUrl != null) { + return this.notificationsUrl; + } + + if (this.baseUrl != null) { + return this.baseUrl + "/notifications"; + } + + return "https://notifications.bitwarden.com"; + } + + getWebVaultUrl() { + if (this.webVaultUrl != null) { + return this.webVaultUrl; + } + + if (this.baseUrl) { + return this.baseUrl; + } + return "https://vault.bitwarden.com"; + } + + getSendUrl() { + return this.getWebVaultUrl() === "https://vault.bitwarden.com" + ? "https://send.bitwarden.com/#" + : this.getWebVaultUrl() + "/#/send/"; + } + + getIconsUrl() { + if (this.iconsUrl != null) { + return this.iconsUrl; + } + + if (this.baseUrl) { + return this.baseUrl + "/icons"; + } + + return "https://icons.bitwarden.net"; + } + + getApiUrl() { + if (this.apiUrl != null) { + return this.apiUrl; + } + + if (this.baseUrl) { + return this.baseUrl + "/api"; + } + + return "https://api.bitwarden.com"; + } + + getIdentityUrl() { + if (this.identityUrl != null) { + return this.identityUrl; + } + + if (this.baseUrl) { + return this.baseUrl + "/identity"; + } + + return "https://identity.bitwarden.com"; + } + + getEventsUrl() { + if (this.eventsUrl != null) { + return this.eventsUrl; + } + + if (this.baseUrl) { + return this.baseUrl + "/events"; + } + + return "https://events.bitwarden.com"; + } + + getKeyConnectorUrl() { + return this.keyConnectorUrl; + } + + async setUrlsFromStorage(): Promise { + const urls: any = await this.stateService.getEnvironmentUrls(); + const envUrls = new EnvironmentUrls(); + + this.baseUrl = envUrls.base = urls.base; + this.webVaultUrl = urls.webVault; + this.apiUrl = envUrls.api = urls.api; + this.identityUrl = envUrls.identity = urls.identity; + this.iconsUrl = urls.icons; + this.notificationsUrl = urls.notifications; + this.eventsUrl = envUrls.events = urls.events; + this.keyConnectorUrl = urls.keyConnector; + } + + async setUrls(urls: Urls): Promise { + urls.base = this.formatUrl(urls.base); + urls.webVault = this.formatUrl(urls.webVault); + urls.api = this.formatUrl(urls.api); + urls.identity = this.formatUrl(urls.identity); + urls.icons = this.formatUrl(urls.icons); + urls.notifications = this.formatUrl(urls.notifications); + urls.events = this.formatUrl(urls.events); + urls.keyConnector = this.formatUrl(urls.keyConnector); + + await this.stateService.setEnvironmentUrls({ + base: urls.base, + api: urls.api, + identity: urls.identity, + webVault: urls.webVault, + icons: urls.icons, + notifications: urls.notifications, + events: urls.events, + keyConnector: urls.keyConnector, + }); + + this.baseUrl = urls.base; + this.webVaultUrl = urls.webVault; + this.apiUrl = urls.api; + this.identityUrl = urls.identity; + this.iconsUrl = urls.icons; + this.notificationsUrl = urls.notifications; + this.eventsUrl = urls.events; + this.keyConnectorUrl = urls.keyConnector; + + this.urlsSubject.next(urls); + + return urls; + } + + getUrls() { + return { + base: this.baseUrl, + webVault: this.webVaultUrl, + api: this.apiUrl, + identity: this.identityUrl, + icons: this.iconsUrl, + notifications: this.notificationsUrl, + events: this.eventsUrl, + keyConnector: this.keyConnectorUrl, + }; + } + + private formatUrl(url: string): string { + if (url == null || url === "") { + return null; + } + + url = url.replace(/\/+$/g, ""); + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "https://" + url; + } + + return url.trim(); + } +} diff --git a/jslib/common/src/services/event.service.ts b/jslib/common/src/services/event.service.ts new file mode 100644 index 00000000..c0024d49 --- /dev/null +++ b/jslib/common/src/services/event.service.ts @@ -0,0 +1,99 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { EventService as EventServiceAbstraction } from "../abstractions/event.service"; +import { LogService } from "../abstractions/log.service"; +import { OrganizationService } from "../abstractions/organization.service"; +import { StateService } from "../abstractions/state.service"; +import { EventType } from "../enums/eventType"; +import { EventData } from "../models/data/eventData"; +import { EventRequest } from "../models/request/eventRequest"; + +export class EventService implements EventServiceAbstraction { + private inited = false; + + constructor( + private apiService: ApiService, + private cipherService: CipherService, + private stateService: StateService, + private logService: LogService, + private organizationService: OrganizationService + ) {} + + init(checkOnInterval: boolean) { + if (this.inited) { + return; + } + + this.inited = true; + if (checkOnInterval) { + this.uploadEvents(); + setInterval(() => this.uploadEvents(), 60 * 1000); // check every 60 seconds + } + } + + async collect( + eventType: EventType, + cipherId: string = null, + uploadImmediately = false + ): Promise { + const authed = await this.stateService.getIsAuthenticated(); + if (!authed) { + return; + } + const organizations = await this.organizationService.getAll(); + if (organizations == null) { + return; + } + const orgIds = new Set(organizations.filter((o) => o.useEvents).map((o) => o.id)); + if (orgIds.size === 0) { + return; + } + if (cipherId != null) { + const cipher = await this.cipherService.get(cipherId); + if (cipher == null || cipher.organizationId == null || !orgIds.has(cipher.organizationId)) { + return; + } + } + let eventCollection = await this.stateService.getEventCollection(); + if (eventCollection == null) { + eventCollection = []; + } + const event = new EventData(); + event.type = eventType; + event.cipherId = cipherId; + event.date = new Date().toISOString(); + eventCollection.push(event); + await this.stateService.setEventCollection(eventCollection); + if (uploadImmediately) { + await this.uploadEvents(); + } + } + + async uploadEvents(userId?: string): Promise { + const authed = await this.stateService.getIsAuthenticated({ userId: userId }); + if (!authed) { + return; + } + const eventCollection = await this.stateService.getEventCollection({ userId: userId }); + if (eventCollection == null || eventCollection.length === 0) { + return; + } + const request = eventCollection.map((e) => { + const req = new EventRequest(); + req.type = e.type; + req.cipherId = e.cipherId; + req.date = e.date; + return req; + }); + try { + await this.apiService.postEventsCollect(request); + this.clearEvents(userId); + } catch (e) { + this.logService.error(e); + } + } + + async clearEvents(userId?: string): Promise { + await this.stateService.setEventCollection(null, { userId: userId }); + } +} diff --git a/jslib/common/src/services/export.service.ts b/jslib/common/src/services/export.service.ts new file mode 100644 index 00000000..55f9206f --- /dev/null +++ b/jslib/common/src/services/export.service.ts @@ -0,0 +1,444 @@ +import * as papa from "papaparse"; + +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { + ExportFormat, + ExportService as ExportServiceAbstraction, +} from "../abstractions/export.service"; +import { FolderService } from "../abstractions/folder.service"; +import { CipherType } from "../enums/cipherType"; +import { DEFAULT_KDF_ITERATIONS, KdfType } from "../enums/kdfType"; +import { Utils } from "../misc/utils"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { Cipher } from "../models/domain/cipher"; +import { Collection } from "../models/domain/collection"; +import { Folder } from "../models/domain/folder"; +import { CipherWithIds as CipherExport } from "../models/export/cipherWithIds"; +import { CollectionWithId as CollectionExport } from "../models/export/collectionWithId"; +import { Event } from "../models/export/event"; +import { FolderWithId as FolderExport } from "../models/export/folderWithId"; +import { CollectionDetailsResponse } from "../models/response/collectionResponse"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { EventView } from "../models/view/eventView"; +import { FolderView } from "../models/view/folderView"; + +export class ExportService implements ExportServiceAbstraction { + constructor( + private folderService: FolderService, + private cipherService: CipherService, + private apiService: ApiService, + private cryptoService: CryptoService, + private cryptoFunctionService: CryptoFunctionService + ) {} + + async getExport(format: ExportFormat = "csv", organizationId?: string): Promise { + if (organizationId) { + return await this.getOrganizationExport(organizationId, format); + } + + if (format === "encrypted_json") { + return this.getEncryptedExport(); + } else { + return this.getDecryptedExport(format); + } + } + + async getPasswordProtectedExport(password: string, organizationId?: string): Promise { + const clearText = organizationId + ? await this.getOrganizationExport(organizationId, "json") + : await this.getExport("json"); + + const salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16)); + const kdfIterations = DEFAULT_KDF_ITERATIONS; + const key = await this.cryptoService.makePinKey( + password, + salt, + KdfType.PBKDF2_SHA256, + kdfIterations + ); + + const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), key); + const encText = await this.cryptoService.encrypt(clearText, key); + + const jsonDoc: any = { + encrypted: true, + passwordProtected: true, + salt: salt, + kdfIterations: kdfIterations, + kdfType: KdfType.PBKDF2_SHA256, + encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, + data: encText.encryptedString, + }; + + return JSON.stringify(jsonDoc, null, " "); + } + + async getOrganizationExport( + organizationId: string, + format: ExportFormat = "csv" + ): Promise { + if (format === "encrypted_json") { + return this.getOrganizationEncryptedExport(organizationId); + } else { + return this.getOrganizationDecryptedExport(organizationId, format); + } + } + + async getEventExport(events: EventView[]): Promise { + return papa.unparse(events.map((e) => new Event(e))); + } + + getFileName(prefix: string = null, extension = "csv"): string { + const now = new Date(); + const dateString = + now.getFullYear() + + "" + + this.padNumber(now.getMonth() + 1, 2) + + "" + + this.padNumber(now.getDate(), 2) + + this.padNumber(now.getHours(), 2) + + "" + + this.padNumber(now.getMinutes(), 2) + + this.padNumber(now.getSeconds(), 2); + + return "bitwarden" + (prefix ? "_" + prefix : "") + "_export_" + dateString + "." + extension; + } + + private async getDecryptedExport(format: "json" | "csv"): Promise { + let decFolders: FolderView[] = []; + let decCiphers: CipherView[] = []; + const promises = []; + + promises.push( + this.folderService.getAllDecrypted().then((folders) => { + decFolders = folders; + }) + ); + + promises.push( + this.cipherService.getAllDecrypted().then((ciphers) => { + decCiphers = ciphers.filter((f) => f.deletedDate == null); + }) + ); + + await Promise.all(promises); + + if (format === "csv") { + const foldersMap = new Map(); + decFolders.forEach((f) => { + if (f.id != null) { + foldersMap.set(f.id, f); + } + }); + + const exportCiphers: any[] = []; + decCiphers.forEach((c) => { + // only export logins and secure notes + if (c.type !== CipherType.Login && c.type !== CipherType.SecureNote) { + return; + } + if (c.organizationId != null) { + return; + } + + const cipher: any = {}; + cipher.folder = + c.folderId != null && foldersMap.has(c.folderId) ? foldersMap.get(c.folderId).name : null; + cipher.favorite = c.favorite ? 1 : null; + this.buildCommonCipher(cipher, c); + exportCiphers.push(cipher); + }); + + return papa.unparse(exportCiphers); + } else { + const jsonDoc: any = { + encrypted: false, + folders: [], + items: [], + }; + + decFolders.forEach((f) => { + if (f.id == null) { + return; + } + const folder = new FolderExport(); + folder.build(f); + jsonDoc.folders.push(folder); + }); + + decCiphers.forEach((c) => { + if (c.organizationId != null) { + return; + } + const cipher = new CipherExport(); + cipher.build(c); + cipher.collectionIds = null; + jsonDoc.items.push(cipher); + }); + + return JSON.stringify(jsonDoc, null, " "); + } + } + + private async getEncryptedExport(): Promise { + let folders: Folder[] = []; + let ciphers: Cipher[] = []; + const promises = []; + + promises.push( + this.folderService.getAll().then((f) => { + folders = f; + }) + ); + + promises.push( + this.cipherService.getAll().then((c) => { + ciphers = c.filter((f) => f.deletedDate == null); + }) + ); + + await Promise.all(promises); + + const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid()); + + const jsonDoc: any = { + encrypted: true, + encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, + folders: [], + items: [], + }; + + folders.forEach((f) => { + if (f.id == null) { + return; + } + const folder = new FolderExport(); + folder.build(f); + jsonDoc.folders.push(folder); + }); + + ciphers.forEach((c) => { + if (c.organizationId != null) { + return; + } + const cipher = new CipherExport(); + cipher.build(c); + cipher.collectionIds = null; + jsonDoc.items.push(cipher); + }); + + return JSON.stringify(jsonDoc, null, " "); + } + + private async getOrganizationDecryptedExport( + organizationId: string, + format: "json" | "csv" + ): Promise { + const decCollections: CollectionView[] = []; + const decCiphers: CipherView[] = []; + const promises = []; + + promises.push( + this.apiService.getCollections(organizationId).then((collections) => { + const collectionPromises: any = []; + if (collections != null && collections.data != null && collections.data.length > 0) { + collections.data.forEach((c) => { + const collection = new Collection(new CollectionData(c as CollectionDetailsResponse)); + collectionPromises.push( + collection.decrypt().then((decCol) => { + decCollections.push(decCol); + }) + ); + }); + } + return Promise.all(collectionPromises); + }) + ); + + promises.push( + this.apiService.getCiphersOrganization(organizationId).then((ciphers) => { + const cipherPromises: any = []; + if (ciphers != null && ciphers.data != null && ciphers.data.length > 0) { + ciphers.data + .filter((c) => c.deletedDate === null) + .forEach((c) => { + const cipher = new Cipher(new CipherData(c)); + cipherPromises.push( + cipher.decrypt().then((decCipher) => { + decCiphers.push(decCipher); + }) + ); + }); + } + return Promise.all(cipherPromises); + }) + ); + + await Promise.all(promises); + + if (format === "csv") { + const collectionsMap = new Map(); + decCollections.forEach((c) => { + collectionsMap.set(c.id, c); + }); + + const exportCiphers: any[] = []; + decCiphers.forEach((c) => { + // only export logins and secure notes + if (c.type !== CipherType.Login && c.type !== CipherType.SecureNote) { + return; + } + + const cipher: any = {}; + cipher.collections = []; + if (c.collectionIds != null) { + cipher.collections = c.collectionIds + .filter((id) => collectionsMap.has(id)) + .map((id) => collectionsMap.get(id).name); + } + this.buildCommonCipher(cipher, c); + exportCiphers.push(cipher); + }); + + return papa.unparse(exportCiphers); + } else { + const jsonDoc: any = { + encrypted: false, + collections: [], + items: [], + }; + + decCollections.forEach((c) => { + const collection = new CollectionExport(); + collection.build(c); + jsonDoc.collections.push(collection); + }); + + decCiphers.forEach((c) => { + const cipher = new CipherExport(); + cipher.build(c); + jsonDoc.items.push(cipher); + }); + return JSON.stringify(jsonDoc, null, " "); + } + } + + private async getOrganizationEncryptedExport(organizationId: string): Promise { + const collections: Collection[] = []; + const ciphers: Cipher[] = []; + const promises = []; + + promises.push( + this.apiService.getCollections(organizationId).then((c) => { + const collectionPromises: any = []; + if (c != null && c.data != null && c.data.length > 0) { + c.data.forEach((r) => { + const collection = new Collection(new CollectionData(r as CollectionDetailsResponse)); + collections.push(collection); + }); + } + return Promise.all(collectionPromises); + }) + ); + + promises.push( + this.apiService.getCiphersOrganization(organizationId).then((c) => { + const cipherPromises: any = []; + if (c != null && c.data != null && c.data.length > 0) { + c.data + .filter((item) => item.deletedDate === null) + .forEach((item) => { + const cipher = new Cipher(new CipherData(item)); + ciphers.push(cipher); + }); + } + return Promise.all(cipherPromises); + }) + ); + + await Promise.all(promises); + + const orgKey = await this.cryptoService.getOrgKey(organizationId); + const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), orgKey); + + const jsonDoc: any = { + encrypted: true, + encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, + collections: [], + items: [], + }; + + collections.forEach((c) => { + const collection = new CollectionExport(); + collection.build(c); + jsonDoc.collections.push(collection); + }); + + ciphers.forEach((c) => { + const cipher = new CipherExport(); + cipher.build(c); + jsonDoc.items.push(cipher); + }); + return JSON.stringify(jsonDoc, null, " "); + } + + private padNumber(num: number, width: number, padCharacter = "0"): string { + const numString = num.toString(); + return numString.length >= width + ? numString + : new Array(width - numString.length + 1).join(padCharacter) + numString; + } + + private buildCommonCipher(cipher: any, c: CipherView) { + cipher.type = null; + cipher.name = c.name; + cipher.notes = c.notes; + cipher.fields = null; + cipher.reprompt = c.reprompt; + // Login props + cipher.login_uri = null; + cipher.login_username = null; + cipher.login_password = null; + cipher.login_totp = null; + + if (c.fields) { + c.fields.forEach((f: any) => { + if (!cipher.fields) { + cipher.fields = ""; + } else { + cipher.fields += "\n"; + } + + cipher.fields += (f.name || "") + ": " + f.value; + }); + } + + switch (c.type) { + case CipherType.Login: + cipher.type = "login"; + cipher.login_username = c.login.username; + cipher.login_password = c.login.password; + cipher.login_totp = c.login.totp; + + if (c.login.uris) { + cipher.login_uri = []; + c.login.uris.forEach((u) => { + cipher.login_uri.push(u.uri); + }); + } + break; + case CipherType.SecureNote: + cipher.type = "note"; + break; + default: + return; + } + + return cipher; + } +} diff --git a/jslib/common/src/services/fileUpload.service.ts b/jslib/common/src/services/fileUpload.service.ts new file mode 100644 index 00000000..6fc0af35 --- /dev/null +++ b/jslib/common/src/services/fileUpload.service.ts @@ -0,0 +1,108 @@ +import { ApiService } from "../abstractions/api.service"; +import { FileUploadService as FileUploadServiceAbstraction } from "../abstractions/fileUpload.service"; +import { LogService } from "../abstractions/log.service"; +import { FileUploadType } from "../enums/fileUploadType"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { SendFileUploadDataResponse } from "../models/response/sendFileUploadDataResponse"; + +import { AzureFileUploadService } from "./azureFileUpload.service"; +import { BitwardenFileUploadService } from "./bitwardenFileUpload.service"; + +export class FileUploadService implements FileUploadServiceAbstraction { + private azureFileUploadService: AzureFileUploadService; + private bitwardenFileUploadService: BitwardenFileUploadService; + + constructor(private logService: LogService, private apiService: ApiService) { + this.azureFileUploadService = new AzureFileUploadService(logService); + this.bitwardenFileUploadService = new BitwardenFileUploadService(apiService); + } + + async uploadSendFile( + uploadData: SendFileUploadDataResponse, + fileName: EncString, + encryptedFileData: EncArrayBuffer + ) { + try { + switch (uploadData.fileUploadType) { + case FileUploadType.Direct: + await this.bitwardenFileUploadService.upload( + fileName.encryptedString, + encryptedFileData, + (fd) => + this.apiService.postSendFile( + uploadData.sendResponse.id, + uploadData.sendResponse.file.id, + fd + ) + ); + break; + case FileUploadType.Azure: { + const renewalCallback = async () => { + const renewalResponse = await this.apiService.renewSendFileUploadUrl( + uploadData.sendResponse.id, + uploadData.sendResponse.file.id + ); + return renewalResponse.url; + }; + await this.azureFileUploadService.upload( + uploadData.url, + encryptedFileData, + renewalCallback + ); + break; + } + default: + throw new Error("Unknown file upload type"); + } + } catch (e) { + await this.apiService.deleteSend(uploadData.sendResponse.id); + throw e; + } + } + + async uploadCipherAttachment( + admin: boolean, + uploadData: AttachmentUploadDataResponse, + encryptedFileName: EncString, + encryptedFileData: EncArrayBuffer + ) { + const response = admin ? uploadData.cipherMiniResponse : uploadData.cipherResponse; + try { + switch (uploadData.fileUploadType) { + case FileUploadType.Direct: + await this.bitwardenFileUploadService.upload( + encryptedFileName.encryptedString, + encryptedFileData, + (fd) => this.apiService.postAttachmentFile(response.id, uploadData.attachmentId, fd) + ); + break; + case FileUploadType.Azure: { + const renewalCallback = async () => { + const renewalResponse = await this.apiService.renewAttachmentUploadUrl( + response.id, + uploadData.attachmentId + ); + return renewalResponse.url; + }; + await this.azureFileUploadService.upload( + uploadData.url, + encryptedFileData, + renewalCallback + ); + break; + } + default: + throw new Error("Unknown file upload type."); + } + } catch (e) { + if (admin) { + await this.apiService.deleteCipherAttachmentAdmin(response.id, uploadData.attachmentId); + } else { + await this.apiService.deleteCipherAttachment(response.id, uploadData.attachmentId); + } + throw e; + } + } +} diff --git a/jslib/common/src/services/folder.service.ts b/jslib/common/src/services/folder.service.ts new file mode 100644 index 00000000..666ca6d2 --- /dev/null +++ b/jslib/common/src/services/folder.service.ts @@ -0,0 +1,194 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FolderService as FolderServiceAbstraction } from "../abstractions/folder.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { StateService } from "../abstractions/state.service"; +import { ServiceUtils } from "../misc/serviceUtils"; +import { Utils } from "../misc/utils"; +import { CipherData } from "../models/data/cipherData"; +import { FolderData } from "../models/data/folderData"; +import { Folder } from "../models/domain/folder"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { TreeNode } from "../models/domain/treeNode"; +import { FolderRequest } from "../models/request/folderRequest"; +import { FolderResponse } from "../models/response/folderResponse"; +import { FolderView } from "../models/view/folderView"; + +const NestingDelimiter = "/"; + +export class FolderService implements FolderServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private apiService: ApiService, + private i18nService: I18nService, + private cipherService: CipherService, + private stateService: StateService + ) {} + + async clearCache(userId?: string): Promise { + await this.stateService.setDecryptedFolders(null, { userId: userId }); + } + + async encrypt(model: FolderView, key?: SymmetricCryptoKey): Promise { + const folder = new Folder(); + folder.id = model.id; + folder.name = await this.cryptoService.encrypt(model.name, key); + return folder; + } + + async get(id: string): Promise { + const folders = await this.stateService.getEncryptedFolders(); + // eslint-disable-next-line + if (folders == null || !folders.hasOwnProperty(id)) { + return null; + } + + return new Folder(folders[id]); + } + + async getAll(): Promise { + const folders = await this.stateService.getEncryptedFolders(); + const response: Folder[] = []; + for (const id in folders) { + // eslint-disable-next-line + if (folders.hasOwnProperty(id)) { + response.push(new Folder(folders[id])); + } + } + return response; + } + + async getAllDecrypted(): Promise { + const decryptedFolders = await this.stateService.getDecryptedFolders(); + if (decryptedFolders != null) { + return decryptedFolders; + } + + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + throw new Error("No key."); + } + + const decFolders: FolderView[] = []; + const promises: Promise[] = []; + const folders = await this.getAll(); + folders.forEach((folder) => { + promises.push(folder.decrypt().then((f) => decFolders.push(f))); + }); + + await Promise.all(promises); + decFolders.sort(Utils.getSortFunction(this.i18nService, "name")); + + const noneFolder = new FolderView(); + noneFolder.name = this.i18nService.t("noneFolder"); + decFolders.push(noneFolder); + + await this.stateService.setDecryptedFolders(decFolders); + return decFolders; + } + + async getAllNested(): Promise[]> { + const folders = await this.getAllDecrypted(); + const nodes: TreeNode[] = []; + folders.forEach((f) => { + const folderCopy = new FolderView(); + folderCopy.id = f.id; + folderCopy.revisionDate = f.revisionDate; + const parts = f.name != null ? f.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : []; + ServiceUtils.nestedTraverse(nodes, 0, parts, folderCopy, null, NestingDelimiter); + }); + return nodes; + } + + async getNested(id: string): Promise> { + const folders = await this.getAllNested(); + return ServiceUtils.getTreeNodeObject(folders, id) as TreeNode; + } + + async saveWithServer(folder: Folder): Promise { + const request = new FolderRequest(folder); + + let response: FolderResponse; + if (folder.id == null) { + response = await this.apiService.postFolder(request); + folder.id = response.id; + } else { + response = await this.apiService.putFolder(folder.id, request); + } + + const userId = await this.stateService.getUserId(); + const data = new FolderData(response, userId); + await this.upsert(data); + } + + async upsert(folder: FolderData | FolderData[]): Promise { + let folders = await this.stateService.getEncryptedFolders(); + if (folders == null) { + folders = {}; + } + + if (folder instanceof FolderData) { + const f = folder as FolderData; + folders[f.id] = f; + } else { + (folder as FolderData[]).forEach((f) => { + folders[f.id] = f; + }); + } + + await this.stateService.setDecryptedFolders(null); + await this.stateService.setEncryptedFolders(folders); + } + + async replace(folders: { [id: string]: FolderData }): Promise { + await this.stateService.setDecryptedFolders(null); + await this.stateService.setEncryptedFolders(folders); + } + + async clear(userId?: string): Promise { + await this.stateService.setDecryptedFolders(null, { userId: userId }); + await this.stateService.setEncryptedFolders(null, { userId: userId }); + } + + async delete(id: string | string[]): Promise { + const folders = await this.stateService.getEncryptedFolders(); + if (folders == null) { + return; + } + + if (typeof id === "string") { + if (folders[id] == null) { + return; + } + delete folders[id]; + } else { + (id as string[]).forEach((i) => { + delete folders[i]; + }); + } + + await this.stateService.setDecryptedFolders(null); + await this.stateService.setEncryptedFolders(folders); + + // Items in a deleted folder are re-assigned to "No Folder" + const ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers != null) { + const updates: CipherData[] = []; + for (const cId in ciphers) { + if (ciphers[cId].folderId === id) { + ciphers[cId].folderId = null; + updates.push(ciphers[cId]); + } + } + if (updates.length > 0) { + this.cipherService.upsert(updates); + } + } + } + + async deleteWithServer(id: string): Promise { + await this.apiService.deleteFolder(id); + await this.delete(id); + } +} diff --git a/jslib/common/src/services/i18n.service.ts b/jslib/common/src/services/i18n.service.ts new file mode 100644 index 00000000..fda8f839 --- /dev/null +++ b/jslib/common/src/services/i18n.service.ts @@ -0,0 +1,175 @@ +import { I18nService as I18nServiceAbstraction } from "../abstractions/i18n.service"; + +export class I18nService implements I18nServiceAbstraction { + locale: string; + // First locale is the default (English) + supportedTranslationLocales: string[] = ["en"]; + translationLocale: string; + collator: Intl.Collator; + localeNames = new Map([ + ["af", "Afrikaans"], + ["az", "Azərbaycanca"], + ["be", "Беларуская"], + ["bg", "български"], + ["bn", "বাংলা"], + ["bs", "bosanski jezik"], + ["ca", "català"], + ["cs", "čeština"], + ["da", "dansk"], + ["de", "Deutsch"], + ["el", "Ελληνικά"], + ["en", "English"], + ["en-GB", "English (British)"], + ["en-IN", "English (India)"], + ["eo", "Esperanto"], + ["es", "español"], + ["et", "eesti"], + ["fa", "فارسی"], + ["fi", "suomi"], + ["fil", "Wikang Filipino"], + ["fr", "français"], + ["he", "עברית"], + ["hi", "हिन्दी"], + ["hr", "hrvatski"], + ["hu", "magyar"], + ["id", "Bahasa Indonesia"], + ["it", "italiano"], + ["ja", "日本語"], + ["ka", "ქართული"], + ["km", "ខ្មែរ, ខេមរភាសា, ភាសាខ្មែរ"], + ["kn", "ಕನ್ನಡ"], + ["ko", "한국어"], + ["lt", "lietuvių kalba"], + ["lv", "Latvietis"], + ["me", "црногорски"], + ["ml", "മലയാളം"], + ["nb", "norsk (bokmål)"], + ["nl", "Nederlands"], + ["nn", "Norsk Nynorsk"], + ["pl", "polski"], + ["pt-BR", "português do Brasil"], + ["pt-PT", "português"], + ["ro", "română"], + ["ru", "русский"], + ["si", "සිංහල"], + ["sk", "slovenčina"], + ["sl", "Slovenski jezik, Slovenščina"], + ["sr", "Српски"], + ["sv", "svenska"], + ["th", "ไทย"], + ["tr", "Türkçe"], + ["uk", "українська"], + ["vi", "Tiếng Việt"], + ["zh-CN", "中文(中国大陆)"], + ["zh-TW", "中文(台灣)"], + ]); + + protected inited: boolean; + protected defaultMessages: any = {}; + protected localeMessages: any = {}; + + constructor( + protected systemLanguage: string, + protected localesDirectory: string, + protected getLocalesJson: (formattedLocale: string) => Promise + ) { + this.systemLanguage = systemLanguage.replace("_", "-"); + } + + async init(locale?: string) { + if (this.inited) { + throw new Error("i18n already initialized."); + } + if (this.supportedTranslationLocales == null || this.supportedTranslationLocales.length === 0) { + throw new Error("supportedTranslationLocales not set."); + } + + this.inited = true; + this.locale = this.translationLocale = locale != null ? locale : this.systemLanguage; + + try { + this.collator = new Intl.Collator(this.locale, { numeric: true, sensitivity: "base" }); + } catch { + this.collator = null; + } + + if (this.supportedTranslationLocales.indexOf(this.translationLocale) === -1) { + this.translationLocale = this.translationLocale.slice(0, 2); + + if (this.supportedTranslationLocales.indexOf(this.translationLocale) === -1) { + this.translationLocale = this.supportedTranslationLocales[0]; + } + } + + if (this.localesDirectory != null) { + await this.loadMessages(this.translationLocale, this.localeMessages); + if (this.translationLocale !== this.supportedTranslationLocales[0]) { + await this.loadMessages(this.supportedTranslationLocales[0], this.defaultMessages); + } + } + } + + t(id: string, p1?: string, p2?: string, p3?: string): string { + return this.translate(id, p1, p2, p3); + } + + translate(id: string, p1?: string, p2?: string, p3?: string): string { + let result: string; + // eslint-disable-next-line + if (this.localeMessages.hasOwnProperty(id) && this.localeMessages[id]) { + result = this.localeMessages[id]; + // eslint-disable-next-line + } else if (this.defaultMessages.hasOwnProperty(id) && this.defaultMessages[id]) { + result = this.defaultMessages[id]; + } else { + result = ""; + } + + if (result !== "") { + if (p1 != null) { + result = result.split("__$1__").join(p1); + } + if (p2 != null) { + result = result.split("__$2__").join(p2); + } + if (p3 != null) { + result = result.split("__$3__").join(p3); + } + } + + return result; + } + + private async loadMessages(locale: string, messagesObj: any): Promise { + const formattedLocale = locale.replace("-", "_"); + const locales = await this.getLocalesJson(formattedLocale); + for (const prop in locales) { + // eslint-disable-next-line + if (!locales.hasOwnProperty(prop)) { + continue; + } + messagesObj[prop] = locales[prop].message; + + if (locales[prop].placeholders) { + for (const placeProp in locales[prop].placeholders) { + if ( + !locales[prop].placeholders.hasOwnProperty(placeProp) || // eslint-disable-line + !locales[prop].placeholders[placeProp].content + ) { + continue; + } + + const replaceToken = "\\$" + placeProp.toUpperCase() + "\\$"; + let replaceContent = locales[prop].placeholders[placeProp].content; + if (replaceContent === "$1" || replaceContent === "$2" || replaceContent === "$3") { + replaceContent = "__$" + replaceContent + "__"; + } + messagesObj[prop] = messagesObj[prop].replace( + new RegExp(replaceToken, "g"), + replaceContent + ); + } + } + } + } +} diff --git a/jslib/common/src/services/import.service.ts b/jslib/common/src/services/import.service.ts new file mode 100644 index 00000000..86226d7d --- /dev/null +++ b/jslib/common/src/services/import.service.ts @@ -0,0 +1,382 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { CollectionService } from "../abstractions/collection.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FolderService } from "../abstractions/folder.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { ImportService as ImportServiceAbstraction } from "../abstractions/import.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { CipherType } from "../enums/cipherType"; +import { + featuredImportOptions, + ImportOption, + ImportType, + regularImportOptions, +} from "../enums/importOptions"; +import { AscendoCsvImporter } from "../importers/ascendoCsvImporter"; +import { AvastCsvImporter } from "../importers/avastCsvImporter"; +import { AvastJsonImporter } from "../importers/avastJsonImporter"; +import { AviraCsvImporter } from "../importers/aviraCsvImporter"; +import { BitwardenCsvImporter } from "../importers/bitwardenCsvImporter"; +import { BitwardenJsonImporter } from "../importers/bitwardenJsonImporter"; +import { BitwardenPasswordProtectedImporter } from "../importers/bitwardenPasswordProtectedImporter"; +import { BlackBerryCsvImporter } from "../importers/blackBerryCsvImporter"; +import { BlurCsvImporter } from "../importers/blurCsvImporter"; +import { ButtercupCsvImporter } from "../importers/buttercupCsvImporter"; +import { ChromeCsvImporter } from "../importers/chromeCsvImporter"; +import { ClipperzHtmlImporter } from "../importers/clipperzHtmlImporter"; +import { CodebookCsvImporter } from "../importers/codebookCsvImporter"; +import { DashlaneCsvImporter } from "../importers/dashlaneImporters/dashlaneCsvImporter"; +import { DashlaneJsonImporter } from "../importers/dashlaneImporters/dashlaneJsonImporter"; +import { EncryptrCsvImporter } from "../importers/encryptrCsvImporter"; +import { EnpassCsvImporter } from "../importers/enpassCsvImporter"; +import { EnpassJsonImporter } from "../importers/enpassJsonImporter"; +import { FirefoxCsvImporter } from "../importers/firefoxCsvImporter"; +import { FSecureFskImporter } from "../importers/fsecureFskImporter"; +import { GnomeJsonImporter } from "../importers/gnomeJsonImporter"; +import { ImportError } from "../importers/importError"; +import { Importer } from "../importers/importer"; +import { KasperskyTxtImporter } from "../importers/kasperskyTxtImporter"; +import { KeePass2XmlImporter } from "../importers/keepass2XmlImporter"; +import { KeePassXCsvImporter } from "../importers/keepassxCsvImporter"; +import { KeeperCsvImporter } from "../importers/keeperImporters/keeperCsvImporter"; +import { LastPassCsvImporter } from "../importers/lastpassCsvImporter"; +import { LogMeOnceCsvImporter } from "../importers/logMeOnceCsvImporter"; +import { MeldiumCsvImporter } from "../importers/meldiumCsvImporter"; +import { MSecureCsvImporter } from "../importers/msecureCsvImporter"; +import { MykiCsvImporter } from "../importers/mykiCsvImporter"; +import { NordPassCsvImporter } from "../importers/nordpassCsvImporter"; +import { OnePassword1PifImporter } from "../importers/onepasswordImporters/onepassword1PifImporter"; +import { OnePassword1PuxImporter } from "../importers/onepasswordImporters/onepassword1PuxImporter"; +import { OnePasswordMacCsvImporter } from "../importers/onepasswordImporters/onepasswordMacCsvImporter"; +import { OnePasswordWinCsvImporter } from "../importers/onepasswordImporters/onepasswordWinCsvImporter"; +import { PadlockCsvImporter } from "../importers/padlockCsvImporter"; +import { PassKeepCsvImporter } from "../importers/passkeepCsvImporter"; +import { PassmanJsonImporter } from "../importers/passmanJsonImporter"; +import { PasspackCsvImporter } from "../importers/passpackCsvImporter"; +import { PasswordAgentCsvImporter } from "../importers/passwordAgentCsvImporter"; +import { PasswordBossJsonImporter } from "../importers/passwordBossJsonImporter"; +import { PasswordDragonXmlImporter } from "../importers/passwordDragonXmlImporter"; +import { PasswordSafeXmlImporter } from "../importers/passwordSafeXmlImporter"; +import { PasswordWalletTxtImporter } from "../importers/passwordWalletTxtImporter"; +import { RememBearCsvImporter } from "../importers/rememBearCsvImporter"; +import { RoboFormCsvImporter } from "../importers/roboformCsvImporter"; +import { SafariCsvImporter } from "../importers/safariCsvImporter"; +import { SafeInCloudXmlImporter } from "../importers/safeInCloudXmlImporter"; +import { SaferPassCsvImporter } from "../importers/saferpassCsvImport"; +import { SecureSafeCsvImporter } from "../importers/secureSafeCsvImporter"; +import { SplashIdCsvImporter } from "../importers/splashIdCsvImporter"; +import { StickyPasswordXmlImporter } from "../importers/stickyPasswordXmlImporter"; +import { TrueKeyCsvImporter } from "../importers/truekeyCsvImporter"; +import { UpmCsvImporter } from "../importers/upmCsvImporter"; +import { YotiCsvImporter } from "../importers/yotiCsvImporter"; +import { ZohoVaultCsvImporter } from "../importers/zohoVaultCsvImporter"; +import { Utils } from "../misc/utils"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherRequest } from "../models/request/cipherRequest"; +import { CollectionRequest } from "../models/request/collectionRequest"; +import { FolderRequest } from "../models/request/folderRequest"; +import { ImportCiphersRequest } from "../models/request/importCiphersRequest"; +import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest"; +import { KvpRequest } from "../models/request/kvpRequest"; +import { ErrorResponse } from "../models/response/errorResponse"; +import { CipherView } from "../models/view/cipherView"; + +export class ImportService implements ImportServiceAbstraction { + featuredImportOptions = featuredImportOptions as readonly ImportOption[]; + + regularImportOptions = regularImportOptions as readonly ImportOption[]; + + constructor( + private cipherService: CipherService, + private folderService: FolderService, + private apiService: ApiService, + private i18nService: I18nService, + private collectionService: CollectionService, + private platformUtilsService: PlatformUtilsService, + private cryptoService: CryptoService + ) {} + + getImportOptions(): ImportOption[] { + return this.featuredImportOptions.concat(this.regularImportOptions); + } + + async import( + importer: Importer, + fileContents: string, + organizationId: string = null + ): Promise { + const importResult = await importer.parse(fileContents); + if (importResult.success) { + if (importResult.folders.length === 0 && importResult.ciphers.length === 0) { + return new ImportError(this.i18nService.t("importNothingError")); + } else if (importResult.ciphers.length > 0) { + const halfway = Math.floor(importResult.ciphers.length / 2); + const last = importResult.ciphers.length - 1; + + if ( + this.badData(importResult.ciphers[0]) && + this.badData(importResult.ciphers[halfway]) && + this.badData(importResult.ciphers[last]) + ) { + return new ImportError(this.i18nService.t("importFormatError")); + } + } + try { + await this.postImport(importResult, organizationId); + } catch (error) { + const errorResponse = new ErrorResponse(error, 400); + return this.handleServerError(errorResponse, importResult); + } + return null; + } else { + if (!Utils.isNullOrWhitespace(importResult.errorMessage)) { + return new ImportError(importResult.errorMessage, importResult.missingPassword); + } else { + return new ImportError( + this.i18nService.t("importFormatError"), + importResult.missingPassword + ); + } + } + } + + getImporter( + format: ImportType | "bitwardenpasswordprotected", + organizationId: string = null, + password: string = null + ): Importer { + const importer = this.getImporterInstance(format, password); + if (importer == null) { + return null; + } + importer.organizationId = organizationId; + return importer; + } + + private getImporterInstance(format: ImportType | "bitwardenpasswordprotected", password: string) { + if (format == null) { + return null; + } + + switch (format) { + case "bitwardencsv": + return new BitwardenCsvImporter(); + case "bitwardenjson": + return new BitwardenJsonImporter(this.cryptoService, this.i18nService); + case "bitwardenpasswordprotected": + return new BitwardenPasswordProtectedImporter( + this.cryptoService, + this.i18nService, + password + ); + case "lastpasscsv": + case "passboltcsv": + return new LastPassCsvImporter(); + case "keepassxcsv": + return new KeePassXCsvImporter(); + case "aviracsv": + return new AviraCsvImporter(); + case "blurcsv": + return new BlurCsvImporter(); + case "safeincloudxml": + return new SafeInCloudXmlImporter(); + case "padlockcsv": + return new PadlockCsvImporter(); + case "keepass2xml": + return new KeePass2XmlImporter(); + case "chromecsv": + case "operacsv": + case "vivaldicsv": + return new ChromeCsvImporter(); + case "firefoxcsv": + return new FirefoxCsvImporter(); + case "upmcsv": + return new UpmCsvImporter(); + case "saferpasscsv": + return new SaferPassCsvImporter(); + case "safaricsv": + return new SafariCsvImporter(); + case "meldiumcsv": + return new MeldiumCsvImporter(); + case "1password1pif": + return new OnePassword1PifImporter(); + case "1password1pux": + return new OnePassword1PuxImporter(); + case "1passwordwincsv": + return new OnePasswordWinCsvImporter(); + case "1passwordmaccsv": + return new OnePasswordMacCsvImporter(); + case "keepercsv": + return new KeeperCsvImporter(); + // case "keeperjson": + // return new KeeperJsonImporter(); + case "passworddragonxml": + return new PasswordDragonXmlImporter(); + case "enpasscsv": + return new EnpassCsvImporter(); + case "enpassjson": + return new EnpassJsonImporter(); + case "pwsafexml": + return new PasswordSafeXmlImporter(); + case "dashlanecsv": + return new DashlaneCsvImporter(); + case "dashlanejson": + return new DashlaneJsonImporter(); + case "msecurecsv": + return new MSecureCsvImporter(); + case "stickypasswordxml": + return new StickyPasswordXmlImporter(); + case "truekeycsv": + return new TrueKeyCsvImporter(); + case "clipperzhtml": + return new ClipperzHtmlImporter(); + case "roboformcsv": + return new RoboFormCsvImporter(); + case "ascendocsv": + return new AscendoCsvImporter(); + case "passwordbossjson": + return new PasswordBossJsonImporter(); + case "zohovaultcsv": + return new ZohoVaultCsvImporter(); + case "splashidcsv": + return new SplashIdCsvImporter(); + case "passkeepcsv": + return new PassKeepCsvImporter(); + case "gnomejson": + return new GnomeJsonImporter(); + case "passwordagentcsv": + return new PasswordAgentCsvImporter(); + case "passpackcsv": + return new PasspackCsvImporter(); + case "passmanjson": + return new PassmanJsonImporter(); + case "avastcsv": + return new AvastCsvImporter(); + case "avastjson": + return new AvastJsonImporter(); + case "fsecurefsk": + return new FSecureFskImporter(); + case "kasperskytxt": + return new KasperskyTxtImporter(); + case "remembearcsv": + return new RememBearCsvImporter(); + case "passwordwallettxt": + return new PasswordWalletTxtImporter(); + case "mykicsv": + return new MykiCsvImporter(); + case "securesafecsv": + return new SecureSafeCsvImporter(); + case "logmeoncecsv": + return new LogMeOnceCsvImporter(); + case "blackberrycsv": + return new BlackBerryCsvImporter(); + case "buttercupcsv": + return new ButtercupCsvImporter(); + case "codebookcsv": + return new CodebookCsvImporter(); + case "encryptrcsv": + return new EncryptrCsvImporter(); + case "yoticsv": + return new YotiCsvImporter(); + case "nordpasscsv": + return new NordPassCsvImporter(); + default: + return null; + } + } + + private async postImport(importResult: ImportResult, organizationId: string = null) { + if (organizationId == null) { + const request = new ImportCiphersRequest(); + for (let i = 0; i < importResult.ciphers.length; i++) { + const c = await this.cipherService.encrypt(importResult.ciphers[i]); + request.ciphers.push(new CipherRequest(c)); + } + if (importResult.folders != null) { + for (let i = 0; i < importResult.folders.length; i++) { + const f = await this.folderService.encrypt(importResult.folders[i]); + request.folders.push(new FolderRequest(f)); + } + } + if (importResult.folderRelationships != null) { + importResult.folderRelationships.forEach((r) => + request.folderRelationships.push(new KvpRequest(r[0], r[1])) + ); + } + return await this.apiService.postImportCiphers(request); + } else { + const request = new ImportOrganizationCiphersRequest(); + for (let i = 0; i < importResult.ciphers.length; i++) { + importResult.ciphers[i].organizationId = organizationId; + const c = await this.cipherService.encrypt(importResult.ciphers[i]); + request.ciphers.push(new CipherRequest(c)); + } + if (importResult.collections != null) { + for (let i = 0; i < importResult.collections.length; i++) { + importResult.collections[i].organizationId = organizationId; + const c = await this.collectionService.encrypt(importResult.collections[i]); + request.collections.push(new CollectionRequest(c)); + } + } + if (importResult.collectionRelationships != null) { + importResult.collectionRelationships.forEach((r) => + request.collectionRelationships.push(new KvpRequest(r[0], r[1])) + ); + } + return await this.apiService.postImportOrganizationCiphers(organizationId, request); + } + } + + private badData(c: CipherView) { + return ( + (c.name == null || c.name === "--") && + c.type === CipherType.Login && + c.login != null && + Utils.isNullOrWhitespace(c.login.password) + ); + } + + private handleServerError(errorResponse: ErrorResponse, importResult: ImportResult): ImportError { + if (errorResponse.validationErrors == null) { + return new ImportError(errorResponse.message); + } + + let errorMessage = ""; + + Object.entries(errorResponse.validationErrors).forEach(([key, value], index) => { + let item; + let itemType; + const i = Number(key.match(/[0-9]+/)[0]); + + switch (key.match(/^\w+/)[0]) { + case "Ciphers": + item = importResult.ciphers[i]; + itemType = CipherType[item.type]; + break; + case "Folders": + item = importResult.folders[i]; + itemType = "Folder"; + break; + case "Collections": + item = importResult.collections[i]; + itemType = "Collection"; + break; + default: + return; + } + + if (index > 0) { + errorMessage += "\n\n"; + } + + if (itemType !== "Folder" && itemType !== "Collection") { + errorMessage += "[" + (i + 1) + "] "; + } + + errorMessage += "[" + itemType + '] "' + item.name + '": ' + value; + }); + + return new ImportError(errorMessage); + } +} diff --git a/jslib/common/src/services/keyConnector.service.ts b/jslib/common/src/services/keyConnector.service.ts new file mode 100644 index 00000000..c7e8b991 --- /dev/null +++ b/jslib/common/src/services/keyConnector.service.ts @@ -0,0 +1,134 @@ +import { ApiService } from "../abstractions/api.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/keyConnector.service"; +import { LogService } from "../abstractions/log.service"; +import { OrganizationService } from "../abstractions/organization.service"; +import { StateService } from "../abstractions/state.service"; +import { TokenService } from "../abstractions/token.service"; +import { OrganizationUserType } from "../enums/organizationUserType"; +import { Utils } from "../misc/utils"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest"; +import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest"; +import { KeysRequest } from "../models/request/keysRequest"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; + +export class KeyConnectorService implements KeyConnectorServiceAbstraction { + constructor( + private stateService: StateService, + private cryptoService: CryptoService, + private apiService: ApiService, + private tokenService: TokenService, + private logService: LogService, + private organizationService: OrganizationService, + private cryptoFunctionService: CryptoFunctionService + ) {} + + setUsesKeyConnector(usesKeyConnector: boolean) { + return this.stateService.setUsesKeyConnector(usesKeyConnector); + } + + async getUsesKeyConnector(): Promise { + return await this.stateService.getUsesKeyConnector(); + } + + async userNeedsMigration() { + const loggedInUsingSso = await this.tokenService.getIsExternal(); + const requiredByOrganization = (await this.getManagingOrganization()) != null; + const userIsNotUsingKeyConnector = !(await this.getUsesKeyConnector()); + + return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector; + } + + async migrateUser() { + const organization = await this.getManagingOrganization(); + const key = await this.cryptoService.getKey(); + const keyConnectorRequest = new KeyConnectorUserKeyRequest(key.encKeyB64); + + try { + await this.apiService.postUserKeyToKeyConnector( + organization.keyConnectorUrl, + keyConnectorRequest + ); + } catch (e) { + throw new Error("Unable to reach key connector"); + } + + await this.apiService.postConvertToKeyConnector(); + } + + async getAndSetKey(url: string) { + try { + const userKeyResponse = await this.apiService.getUserKeyFromKeyConnector(url); + const keyArr = Utils.fromB64ToArray(userKeyResponse.key); + const k = new SymmetricCryptoKey(keyArr); + await this.cryptoService.setKey(k); + } catch (e) { + this.logService.error(e); + throw new Error("Unable to reach key connector"); + } + } + + async getManagingOrganization() { + const orgs = await this.organizationService.getAll(); + return orgs.find( + (o) => + o.keyConnectorEnabled && + o.type !== OrganizationUserType.Admin && + o.type !== OrganizationUserType.Owner && + !o.isProviderUser + ); + } + + async convertNewSsoUserToKeyConnector(tokenResponse: IdentityTokenResponse, orgId: string) { + const { kdf, kdfIterations, keyConnectorUrl } = tokenResponse; + const password = await this.cryptoFunctionService.randomBytes(64); + + const k = await this.cryptoService.makeKey( + Utils.fromBufferToB64(password), + await this.tokenService.getEmail(), + kdf, + kdfIterations + ); + const keyConnectorRequest = new KeyConnectorUserKeyRequest(k.encKeyB64); + await this.cryptoService.setKey(k); + + const encKey = await this.cryptoService.makeEncKey(k); + await this.cryptoService.setEncKey(encKey[1].encryptedString); + + const [pubKey, privKey] = await this.cryptoService.makeKeyPair(); + + try { + await this.apiService.postUserKeyToKeyConnector(keyConnectorUrl, keyConnectorRequest); + } catch (e) { + throw new Error("Unable to reach key connector"); + } + + const keys = new KeysRequest(pubKey, privKey.encryptedString); + const setPasswordRequest = new SetKeyConnectorKeyRequest( + encKey[1].encryptedString, + kdf, + kdfIterations, + orgId, + keys + ); + await this.apiService.postSetKeyConnectorKey(setPasswordRequest); + } + + async setConvertAccountRequired(status: boolean) { + await this.stateService.setConvertAccountToKeyConnector(status); + } + + async getConvertAccountRequired(): Promise { + return await this.stateService.getConvertAccountToKeyConnector(); + } + + async removeConvertAccountRequired() { + await this.stateService.setConvertAccountToKeyConnector(null); + } + + async clear() { + await this.removeConvertAccountRequired(); + } +} diff --git a/jslib/common/src/services/noopMessaging.service.ts b/jslib/common/src/services/noopMessaging.service.ts new file mode 100644 index 00000000..d1a60bc5 --- /dev/null +++ b/jslib/common/src/services/noopMessaging.service.ts @@ -0,0 +1,7 @@ +import { MessagingService } from "../abstractions/messaging.service"; + +export class NoopMessagingService implements MessagingService { + send(subscriber: string, arg: any = {}) { + // Do nothing... + } +} diff --git a/jslib/common/src/services/notifications.service.ts b/jslib/common/src/services/notifications.service.ts new file mode 100644 index 00000000..d4e3fef4 --- /dev/null +++ b/jslib/common/src/services/notifications.service.ts @@ -0,0 +1,231 @@ +import * as signalR from "@microsoft/signalr"; +import * as signalRMsgPack from "@microsoft/signalr-protocol-msgpack"; + +import { ApiService } from "../abstractions/api.service"; +import { AppIdService } from "../abstractions/appId.service"; +import { EnvironmentService } from "../abstractions/environment.service"; +import { LogService } from "../abstractions/log.service"; +import { NotificationsService as NotificationsServiceAbstraction } from "../abstractions/notifications.service"; +import { StateService } from "../abstractions/state.service"; +import { SyncService } from "../abstractions/sync.service"; +import { VaultTimeoutService } from "../abstractions/vaultTimeout.service"; +import { NotificationType } from "../enums/notificationType"; +import { + NotificationResponse, + SyncCipherNotification, + SyncFolderNotification, + SyncSendNotification, +} from "../models/response/notificationResponse"; + +export class NotificationsService implements NotificationsServiceAbstraction { + private signalrConnection: signalR.HubConnection; + private url: string; + private connected = false; + private inited = false; + private inactive = false; + private reconnectTimer: any = null; + + constructor( + private syncService: SyncService, + private appIdService: AppIdService, + private apiService: ApiService, + private vaultTimeoutService: VaultTimeoutService, + private environmentService: EnvironmentService, + private logoutCallback: () => Promise, + private logService: LogService, + private stateService: StateService + ) { + this.environmentService.urls.subscribe(() => { + if (!this.inited) { + return; + } + + this.init(); + }); + } + + async init(): Promise { + this.inited = false; + this.url = this.environmentService.getNotificationsUrl(); + + // Set notifications server URL to `https://-` to effectively disable communication + // with the notifications server from the client app + if (this.url === "https://-") { + return; + } + + if (this.signalrConnection != null) { + this.signalrConnection.off("ReceiveMessage"); + this.signalrConnection.off("Heartbeat"); + await this.signalrConnection.stop(); + this.connected = false; + this.signalrConnection = null; + } + + this.signalrConnection = new signalR.HubConnectionBuilder() + .withUrl(this.url + "/hub", { + accessTokenFactory: () => this.apiService.getActiveBearerToken(), + skipNegotiation: true, + transport: signalR.HttpTransportType.WebSockets, + }) + .withHubProtocol(new signalRMsgPack.MessagePackHubProtocol() as signalR.IHubProtocol) + // .configureLogging(signalR.LogLevel.Trace) + .build(); + + this.signalrConnection.on("ReceiveMessage", (data: any) => + this.processNotification(new NotificationResponse(data)) + ); + // eslint-disable-next-line + this.signalrConnection.on("Heartbeat", (data: any) => { + /*console.log('Heartbeat!');*/ + }); + this.signalrConnection.onclose(() => { + this.connected = false; + this.reconnect(true); + }); + this.inited = true; + if (await this.isAuthedAndUnlocked()) { + await this.reconnect(false); + } + } + + async updateConnection(sync = false): Promise { + if (!this.inited) { + return; + } + try { + if (await this.isAuthedAndUnlocked()) { + await this.reconnect(sync); + } else { + await this.signalrConnection.stop(); + } + } catch (e) { + this.logService.error(e.toString()); + } + } + + async reconnectFromActivity(): Promise { + this.inactive = false; + if (this.inited && !this.connected) { + await this.reconnect(true); + } + } + + async disconnectFromInactivity(): Promise { + this.inactive = true; + if (this.inited && this.connected) { + await this.signalrConnection.stop(); + } + } + + private async processNotification(notification: NotificationResponse) { + const appId = await this.appIdService.getAppId(); + if (notification == null || notification.contextId === appId) { + return; + } + + const isAuthenticated = await this.stateService.getIsAuthenticated(); + const payloadUserId = notification.payload.userId || notification.payload.UserId; + const myUserId = await this.stateService.getUserId(); + if (isAuthenticated && payloadUserId != null && payloadUserId !== myUserId) { + return; + } + + switch (notification.type) { + case NotificationType.SyncCipherCreate: + case NotificationType.SyncCipherUpdate: + await this.syncService.syncUpsertCipher( + notification.payload as SyncCipherNotification, + notification.type === NotificationType.SyncCipherUpdate + ); + break; + case NotificationType.SyncCipherDelete: + case NotificationType.SyncLoginDelete: + await this.syncService.syncDeleteCipher(notification.payload as SyncCipherNotification); + break; + case NotificationType.SyncFolderCreate: + case NotificationType.SyncFolderUpdate: + await this.syncService.syncUpsertFolder( + notification.payload as SyncFolderNotification, + notification.type === NotificationType.SyncFolderUpdate + ); + break; + case NotificationType.SyncFolderDelete: + await this.syncService.syncDeleteFolder(notification.payload as SyncFolderNotification); + break; + case NotificationType.SyncVault: + case NotificationType.SyncCiphers: + case NotificationType.SyncSettings: + if (isAuthenticated) { + await this.syncService.fullSync(false); + } + break; + case NotificationType.SyncOrgKeys: + if (isAuthenticated) { + await this.syncService.fullSync(true); + // Stop so a reconnect can be made + await this.signalrConnection.stop(); + } + break; + case NotificationType.LogOut: + if (isAuthenticated) { + this.logoutCallback(); + } + break; + case NotificationType.SyncSendCreate: + case NotificationType.SyncSendUpdate: + await this.syncService.syncUpsertSend( + notification.payload as SyncSendNotification, + notification.type === NotificationType.SyncSendUpdate + ); + break; + case NotificationType.SyncSendDelete: + await this.syncService.syncDeleteSend(notification.payload as SyncSendNotification); + break; + default: + break; + } + } + + private async reconnect(sync: boolean) { + if (this.reconnectTimer != null) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + if (this.connected || !this.inited || this.inactive) { + return; + } + const authedAndUnlocked = await this.isAuthedAndUnlocked(); + if (!authedAndUnlocked) { + return; + } + + try { + await this.signalrConnection.start(); + this.connected = true; + if (sync) { + await this.syncService.fullSync(false); + } + } catch (e) { + this.logService.error(e); + } + + if (!this.connected) { + this.reconnectTimer = setTimeout(() => this.reconnect(sync), this.random(120000, 300000)); + } + } + + private async isAuthedAndUnlocked() { + if (await this.stateService.getIsAuthenticated()) { + const locked = await this.vaultTimeoutService.isLocked(); + return !locked; + } + return false; + } + + private random(min: number, max: number) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; + } +} diff --git a/jslib/common/src/services/organization.service.ts b/jslib/common/src/services/organization.service.ts new file mode 100644 index 00000000..0ff32e81 --- /dev/null +++ b/jslib/common/src/services/organization.service.ts @@ -0,0 +1,55 @@ +import { OrganizationService as OrganizationServiceAbstraction } from "../abstractions/organization.service"; +import { StateService } from "../abstractions/state.service"; +import { OrganizationData } from "../models/data/organizationData"; +import { Organization } from "../models/domain/organization"; + +export class OrganizationService implements OrganizationServiceAbstraction { + constructor(private stateService: StateService) {} + + async get(id: string): Promise { + const organizations = await this.stateService.getOrganizations(); + // eslint-disable-next-line + if (organizations == null || !organizations.hasOwnProperty(id)) { + return null; + } + + return new Organization(organizations[id]); + } + + async getByIdentifier(identifier: string): Promise { + const organizations = await this.getAll(); + if (organizations == null || organizations.length === 0) { + return null; + } + + return organizations.find((o) => o.identifier === identifier); + } + + async getAll(userId?: string): Promise { + const organizations = await this.stateService.getOrganizations({ userId: userId }); + const response: Organization[] = []; + for (const id in organizations) { + // eslint-disable-next-line + if (organizations.hasOwnProperty(id) && !organizations[id].isProviderUser) { + response.push(new Organization(organizations[id])); + } + } + return response; + } + + async save(organizations: { [id: string]: OrganizationData }) { + return await this.stateService.setOrganizations(organizations); + } + + async canManageSponsorships(): Promise { + const orgs = await this.getAll(); + return orgs.some( + (o) => o.familySponsorshipAvailable || o.familySponsorshipFriendlyName !== null + ); + } + + async hasOrganizations(userId?: string): Promise { + const organizations = await this.getAll(userId); + return organizations.length > 0; + } +} diff --git a/jslib/common/src/services/passwordGeneration.service.ts b/jslib/common/src/services/passwordGeneration.service.ts new file mode 100644 index 00000000..c752fcd9 --- /dev/null +++ b/jslib/common/src/services/passwordGeneration.service.ts @@ -0,0 +1,572 @@ +import * as zxcvbn from "zxcvbn"; + +import { CryptoService } from "../abstractions/crypto.service"; +import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "../abstractions/passwordGeneration.service"; +import { PolicyService } from "../abstractions/policy.service"; +import { StateService } from "../abstractions/state.service"; +import { PolicyType } from "../enums/policyType"; +import { EEFLongWordList } from "../misc/wordlist"; +import { EncString } from "../models/domain/encString"; +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { PasswordGeneratorPolicyOptions } from "../models/domain/passwordGeneratorPolicyOptions"; +import { Policy } from "../models/domain/policy"; + +const DefaultOptions = { + length: 14, + ambiguous: false, + number: true, + minNumber: 1, + uppercase: true, + minUppercase: 0, + lowercase: true, + minLowercase: 0, + special: false, + minSpecial: 1, + type: "password", + numWords: 3, + wordSeparator: "-", + capitalize: false, + includeNumber: false, +}; + +const MaxPasswordsInHistory = 100; + +export class PasswordGenerationService implements PasswordGenerationServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private policyService: PolicyService, + private stateService: StateService + ) {} + + async generatePassword(options: any): Promise { + // overload defaults with given options + const o = Object.assign({}, DefaultOptions, options); + + if (o.type === "passphrase") { + return this.generatePassphrase(options); + } + + // sanitize + this.sanitizePasswordLength(o, true); + + const minLength: number = o.minUppercase + o.minLowercase + o.minNumber + o.minSpecial; + if (o.length < minLength) { + o.length = minLength; + } + + const positions: string[] = []; + if (o.lowercase && o.minLowercase > 0) { + for (let i = 0; i < o.minLowercase; i++) { + positions.push("l"); + } + } + if (o.uppercase && o.minUppercase > 0) { + for (let i = 0; i < o.minUppercase; i++) { + positions.push("u"); + } + } + if (o.number && o.minNumber > 0) { + for (let i = 0; i < o.minNumber; i++) { + positions.push("n"); + } + } + if (o.special && o.minSpecial > 0) { + for (let i = 0; i < o.minSpecial; i++) { + positions.push("s"); + } + } + while (positions.length < o.length) { + positions.push("a"); + } + + // shuffle + await this.shuffleArray(positions); + + // build out the char sets + let allCharSet = ""; + + let lowercaseCharSet = "abcdefghijkmnopqrstuvwxyz"; + if (o.ambiguous) { + lowercaseCharSet += "l"; + } + if (o.lowercase) { + allCharSet += lowercaseCharSet; + } + + let uppercaseCharSet = "ABCDEFGHJKLMNPQRSTUVWXYZ"; + if (o.ambiguous) { + uppercaseCharSet += "IO"; + } + if (o.uppercase) { + allCharSet += uppercaseCharSet; + } + + let numberCharSet = "23456789"; + if (o.ambiguous) { + numberCharSet += "01"; + } + if (o.number) { + allCharSet += numberCharSet; + } + + const specialCharSet = "!@#$%^&*"; + if (o.special) { + allCharSet += specialCharSet; + } + + let password = ""; + for (let i = 0; i < o.length; i++) { + let positionChars: string; + switch (positions[i]) { + case "l": + positionChars = lowercaseCharSet; + break; + case "u": + positionChars = uppercaseCharSet; + break; + case "n": + positionChars = numberCharSet; + break; + case "s": + positionChars = specialCharSet; + break; + case "a": + positionChars = allCharSet; + break; + default: + break; + } + + const randomCharIndex = await this.cryptoService.randomNumber(0, positionChars.length - 1); + password += positionChars.charAt(randomCharIndex); + } + + return password; + } + + async generatePassphrase(options: any): Promise { + const o = Object.assign({}, DefaultOptions, options); + + if (o.numWords == null || o.numWords <= 2) { + o.numWords = DefaultOptions.numWords; + } + if (o.wordSeparator == null || o.wordSeparator.length === 0 || o.wordSeparator.length > 1) { + o.wordSeparator = " "; + } + if (o.capitalize == null) { + o.capitalize = false; + } + if (o.includeNumber == null) { + o.includeNumber = false; + } + + const listLength = EEFLongWordList.length - 1; + const wordList = new Array(o.numWords); + for (let i = 0; i < o.numWords; i++) { + const wordIndex = await this.cryptoService.randomNumber(0, listLength); + if (o.capitalize) { + wordList[i] = this.capitalize(EEFLongWordList[wordIndex]); + } else { + wordList[i] = EEFLongWordList[wordIndex]; + } + } + + if (o.includeNumber) { + await this.appendRandomNumberToRandomWord(wordList); + } + return wordList.join(o.wordSeparator); + } + + async getOptions(): Promise<[any, PasswordGeneratorPolicyOptions]> { + let options = await this.stateService.getPasswordGenerationOptions(); + if (options == null) { + options = Object.assign({}, DefaultOptions); + } else { + options = Object.assign({}, DefaultOptions, options); + } + await this.stateService.setPasswordGenerationOptions(options); + const enforcedOptions = await this.enforcePasswordGeneratorPoliciesOnOptions(options); + options = enforcedOptions[0]; + return [options, enforcedOptions[1]]; + } + + async enforcePasswordGeneratorPoliciesOnOptions( + options: any + ): Promise<[any, PasswordGeneratorPolicyOptions]> { + let enforcedPolicyOptions = await this.getPasswordGeneratorPolicyOptions(); + if (enforcedPolicyOptions != null) { + if (options.length < enforcedPolicyOptions.minLength) { + options.length = enforcedPolicyOptions.minLength; + } + + if (enforcedPolicyOptions.useUppercase) { + options.uppercase = true; + } + + if (enforcedPolicyOptions.useLowercase) { + options.lowercase = true; + } + + if (enforcedPolicyOptions.useNumbers) { + options.number = true; + } + + if (options.minNumber < enforcedPolicyOptions.numberCount) { + options.minNumber = enforcedPolicyOptions.numberCount; + } + + if (enforcedPolicyOptions.useSpecial) { + options.special = true; + } + + if (options.minSpecial < enforcedPolicyOptions.specialCount) { + options.minSpecial = enforcedPolicyOptions.specialCount; + } + + // Must normalize these fields because the receiving call expects all options to pass the current rules + if (options.minSpecial + options.minNumber > options.length) { + options.minSpecial = options.length - options.minNumber; + } + + if (options.numWords < enforcedPolicyOptions.minNumberWords) { + options.numWords = enforcedPolicyOptions.minNumberWords; + } + + if (enforcedPolicyOptions.capitalize) { + options.capitalize = true; + } + + if (enforcedPolicyOptions.includeNumber) { + options.includeNumber = true; + } + + // Force default type if password/passphrase selected via policy + if ( + enforcedPolicyOptions.defaultType === "password" || + enforcedPolicyOptions.defaultType === "passphrase" + ) { + options.type = enforcedPolicyOptions.defaultType; + } + } else { + // UI layer expects an instantiated object to prevent more explicit null checks + enforcedPolicyOptions = new PasswordGeneratorPolicyOptions(); + } + return [options, enforcedPolicyOptions]; + } + + async getPasswordGeneratorPolicyOptions(): Promise { + const policies: Policy[] = + this.policyService == null + ? null + : await this.policyService.getAll(PolicyType.PasswordGenerator); + let enforcedOptions: PasswordGeneratorPolicyOptions = null; + + if (policies == null || policies.length === 0) { + return enforcedOptions; + } + + policies.forEach((currentPolicy) => { + if (!currentPolicy.enabled || currentPolicy.data == null) { + return; + } + + if (enforcedOptions == null) { + enforcedOptions = new PasswordGeneratorPolicyOptions(); + } + + // Password wins in multi-org collisions + if (currentPolicy.data.defaultType != null && enforcedOptions.defaultType !== "password") { + enforcedOptions.defaultType = currentPolicy.data.defaultType; + } + + if ( + currentPolicy.data.minLength != null && + currentPolicy.data.minLength > enforcedOptions.minLength + ) { + enforcedOptions.minLength = currentPolicy.data.minLength; + } + + if (currentPolicy.data.useUpper) { + enforcedOptions.useUppercase = true; + } + + if (currentPolicy.data.useLower) { + enforcedOptions.useLowercase = true; + } + + if (currentPolicy.data.useNumbers) { + enforcedOptions.useNumbers = true; + } + + if ( + currentPolicy.data.minNumbers != null && + currentPolicy.data.minNumbers > enforcedOptions.numberCount + ) { + enforcedOptions.numberCount = currentPolicy.data.minNumbers; + } + + if (currentPolicy.data.useSpecial) { + enforcedOptions.useSpecial = true; + } + + if ( + currentPolicy.data.minSpecial != null && + currentPolicy.data.minSpecial > enforcedOptions.specialCount + ) { + enforcedOptions.specialCount = currentPolicy.data.minSpecial; + } + + if ( + currentPolicy.data.minNumberWords != null && + currentPolicy.data.minNumberWords > enforcedOptions.minNumberWords + ) { + enforcedOptions.minNumberWords = currentPolicy.data.minNumberWords; + } + + if (currentPolicy.data.capitalize) { + enforcedOptions.capitalize = true; + } + + if (currentPolicy.data.includeNumber) { + enforcedOptions.includeNumber = true; + } + }); + + return enforcedOptions; + } + + async saveOptions(options: any) { + await this.stateService.setPasswordGenerationOptions(options); + } + + async getHistory(): Promise { + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + return new Array(); + } + + if ((await this.stateService.getDecryptedPasswordGenerationHistory()) == null) { + const encrypted = await this.stateService.getEncryptedPasswordGenerationHistory(); + const decrypted = await this.decryptHistory(encrypted); + await this.stateService.setDecryptedPasswordGenerationHistory(decrypted); + } + + const passwordGenerationHistory = + await this.stateService.getDecryptedPasswordGenerationHistory(); + return passwordGenerationHistory != null + ? passwordGenerationHistory + : new Array(); + } + + async addHistory(password: string): Promise { + // Cannot add new history if no key is available + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + return; + } + + const currentHistory = await this.getHistory(); + + // Prevent duplicates + if (this.matchesPrevious(password, currentHistory)) { + return; + } + + currentHistory.unshift(new GeneratedPasswordHistory(password, Date.now())); + + // Remove old items. + if (currentHistory.length > MaxPasswordsInHistory) { + currentHistory.pop(); + } + + const newHistory = await this.encryptHistory(currentHistory); + return await this.stateService.setEncryptedPasswordGenerationHistory(newHistory); + } + + async clear(userId?: string): Promise { + await this.stateService.setEncryptedPasswordGenerationHistory(null, { userId: userId }); + await this.stateService.setDecryptedPasswordGenerationHistory(null, { userId: userId }); + } + + passwordStrength(password: string, userInputs: string[] = null): zxcvbn.ZXCVBNResult { + if (password == null || password.length === 0) { + return null; + } + let globalUserInputs = ["bitwarden", "bit", "warden"]; + if (userInputs != null && userInputs.length > 0) { + globalUserInputs = globalUserInputs.concat(userInputs); + } + // Use a hash set to get rid of any duplicate user inputs + const finalUserInputs = Array.from(new Set(globalUserInputs)); + const result = zxcvbn(password, finalUserInputs); + return result; + } + + normalizeOptions(options: any, enforcedPolicyOptions: PasswordGeneratorPolicyOptions) { + options.minLowercase = 0; + options.minUppercase = 0; + + if (!options.length || options.length < 5) { + options.length = 5; + } else if (options.length > 128) { + options.length = 128; + } + + if (options.length < enforcedPolicyOptions.minLength) { + options.length = enforcedPolicyOptions.minLength; + } + + if (!options.minNumber) { + options.minNumber = 0; + } else if (options.minNumber > options.length) { + options.minNumber = options.length; + } else if (options.minNumber > 9) { + options.minNumber = 9; + } + + if (options.minNumber < enforcedPolicyOptions.numberCount) { + options.minNumber = enforcedPolicyOptions.numberCount; + } + + if (!options.minSpecial) { + options.minSpecial = 0; + } else if (options.minSpecial > options.length) { + options.minSpecial = options.length; + } else if (options.minSpecial > 9) { + options.minSpecial = 9; + } + + if (options.minSpecial < enforcedPolicyOptions.specialCount) { + options.minSpecial = enforcedPolicyOptions.specialCount; + } + + if (options.minSpecial + options.minNumber > options.length) { + options.minSpecial = options.length - options.minNumber; + } + + if (options.numWords == null || options.length < 3) { + options.numWords = 3; + } else if (options.numWords > 20) { + options.numWords = 20; + } + + if (options.numWords < enforcedPolicyOptions.minNumberWords) { + options.numWords = enforcedPolicyOptions.minNumberWords; + } + + if (options.wordSeparator != null && options.wordSeparator.length > 1) { + options.wordSeparator = options.wordSeparator[0]; + } + + this.sanitizePasswordLength(options, false); + } + + private capitalize(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + + private async appendRandomNumberToRandomWord(wordList: string[]) { + if (wordList == null || wordList.length <= 0) { + return; + } + const index = await this.cryptoService.randomNumber(0, wordList.length - 1); + const num = await this.cryptoService.randomNumber(0, 9); + wordList[index] = wordList[index] + num; + } + + private async encryptHistory( + history: GeneratedPasswordHistory[] + ): Promise { + if (history == null || history.length === 0) { + return Promise.resolve([]); + } + + const promises = history.map(async (item) => { + const encrypted = await this.cryptoService.encrypt(item.password); + return new GeneratedPasswordHistory(encrypted.encryptedString, item.date); + }); + + return await Promise.all(promises); + } + + private async decryptHistory( + history: GeneratedPasswordHistory[] + ): Promise { + if (history == null || history.length === 0) { + return Promise.resolve([]); + } + + const promises = history.map(async (item) => { + const decrypted = await this.cryptoService.decryptToUtf8(new EncString(item.password)); + return new GeneratedPasswordHistory(decrypted, item.date); + }); + + return await Promise.all(promises); + } + + private matchesPrevious(password: string, history: GeneratedPasswordHistory[]): boolean { + if (history == null || history.length === 0) { + return false; + } + + return history[history.length - 1].password === password; + } + + // ref: https://stackoverflow.com/a/12646864/1090359 + private async shuffleArray(array: string[]) { + for (let i = array.length - 1; i > 0; i--) { + const j = await this.cryptoService.randomNumber(0, i); + [array[i], array[j]] = [array[j], array[i]]; + } + } + + private sanitizePasswordLength(options: any, forGeneration: boolean) { + let minUppercaseCalc = 0; + let minLowercaseCalc = 0; + let minNumberCalc: number = options.minNumber; + let minSpecialCalc: number = options.minSpecial; + + if (options.uppercase && options.minUppercase <= 0) { + minUppercaseCalc = 1; + } else if (!options.uppercase) { + minUppercaseCalc = 0; + } + + if (options.lowercase && options.minLowercase <= 0) { + minLowercaseCalc = 1; + } else if (!options.lowercase) { + minLowercaseCalc = 0; + } + + if (options.number && options.minNumber <= 0) { + minNumberCalc = 1; + } else if (!options.number) { + minNumberCalc = 0; + } + + if (options.special && options.minSpecial <= 0) { + minSpecialCalc = 1; + } else if (!options.special) { + minSpecialCalc = 0; + } + + // This should never happen but is a final safety net + if (!options.length || options.length < 1) { + options.length = 10; + } + + const minLength: number = minUppercaseCalc + minLowercaseCalc + minNumberCalc + minSpecialCalc; + // Normalize and Generation both require this modification + if (options.length < minLength) { + options.length = minLength; + } + + // Apply other changes if the options object passed in is for generation + if (forGeneration) { + options.minUppercase = minUppercaseCalc; + options.minLowercase = minLowercaseCalc; + options.minNumber = minNumberCalc; + options.minSpecial = minSpecialCalc; + } + } +} diff --git a/jslib/common/src/services/policy.service.ts b/jslib/common/src/services/policy.service.ts new file mode 100644 index 00000000..91ab864b --- /dev/null +++ b/jslib/common/src/services/policy.service.ts @@ -0,0 +1,247 @@ +import { ApiService } from "../abstractions/api.service"; +import { OrganizationService } from "../abstractions/organization.service"; +import { PolicyService as PolicyServiceAbstraction } from "../abstractions/policy.service"; +import { StateService } from "../abstractions/state.service"; +import { OrganizationUserStatusType } from "../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../enums/organizationUserType"; +import { PolicyType } from "../enums/policyType"; +import { PolicyData } from "../models/data/policyData"; +import { MasterPasswordPolicyOptions } from "../models/domain/masterPasswordPolicyOptions"; +import { Organization } from "../models/domain/organization"; +import { Policy } from "../models/domain/policy"; +import { ResetPasswordPolicyOptions } from "../models/domain/resetPasswordPolicyOptions"; +import { ListResponse } from "../models/response/listResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; + +export class PolicyService implements PolicyServiceAbstraction { + policyCache: Policy[]; + + constructor( + private stateService: StateService, + private organizationService: OrganizationService, + private apiService: ApiService + ) {} + + async clearCache(): Promise { + await this.stateService.setDecryptedPolicies(null); + } + + async getAll(type?: PolicyType, userId?: string): Promise { + let response: Policy[] = []; + const decryptedPolicies = await this.stateService.getDecryptedPolicies({ userId: userId }); + if (decryptedPolicies != null) { + response = decryptedPolicies; + } else { + const diskPolicies = await this.stateService.getEncryptedPolicies({ userId: userId }); + for (const id in diskPolicies) { + // eslint-disable-next-line + if (diskPolicies.hasOwnProperty(id)) { + response.push(new Policy(diskPolicies[id])); + } + } + await this.stateService.setDecryptedPolicies(response, { userId: userId }); + } + if (type != null) { + return response.filter((policy) => policy.type === type); + } else { + return response; + } + } + + async getPolicyForOrganization(policyType: PolicyType, organizationId: string): Promise { + const org = await this.organizationService.get(organizationId); + if (org?.isProviderUser) { + const orgPolicies = await this.apiService.getPolicies(organizationId); + const policy = orgPolicies.data.find((p) => p.organizationId === organizationId); + + if (policy == null) { + return null; + } + + return new Policy(new PolicyData(policy)); + } + + const policies = await this.getAll(policyType); + return policies.find((p) => p.organizationId === organizationId); + } + + async replace(policies: { [id: string]: PolicyData }): Promise { + await this.stateService.setDecryptedPolicies(null); + await this.stateService.setEncryptedPolicies(policies); + } + + async clear(userId?: string): Promise { + await this.stateService.setDecryptedPolicies(null, { userId: userId }); + await this.stateService.setEncryptedPolicies(null, { userId: userId }); + } + + async getMasterPasswordPoliciesForInvitedUsers( + orgId: string + ): Promise { + const userId = await this.stateService.getUserId(); + const response = await this.apiService.getPoliciesByInvitedUser(orgId, userId); + const policies = await this.mapPoliciesFromToken(response); + return this.getMasterPasswordPolicyOptions(policies); + } + + async getMasterPasswordPolicyOptions(policies?: Policy[]): Promise { + let enforcedOptions: MasterPasswordPolicyOptions = null; + + if (policies == null) { + policies = await this.getAll(PolicyType.MasterPassword); + } else { + policies = policies.filter((p) => p.type === PolicyType.MasterPassword); + } + + if (policies == null || policies.length === 0) { + return enforcedOptions; + } + + policies.forEach((currentPolicy) => { + if (!currentPolicy.enabled || currentPolicy.data == null) { + return; + } + + if (enforcedOptions == null) { + enforcedOptions = new MasterPasswordPolicyOptions(); + } + + if ( + currentPolicy.data.minComplexity != null && + currentPolicy.data.minComplexity > enforcedOptions.minComplexity + ) { + enforcedOptions.minComplexity = currentPolicy.data.minComplexity; + } + + if ( + currentPolicy.data.minLength != null && + currentPolicy.data.minLength > enforcedOptions.minLength + ) { + enforcedOptions.minLength = currentPolicy.data.minLength; + } + + if (currentPolicy.data.requireUpper) { + enforcedOptions.requireUpper = true; + } + + if (currentPolicy.data.requireLower) { + enforcedOptions.requireLower = true; + } + + if (currentPolicy.data.requireNumbers) { + enforcedOptions.requireNumbers = true; + } + + if (currentPolicy.data.requireSpecial) { + enforcedOptions.requireSpecial = true; + } + }); + + return enforcedOptions; + } + + evaluateMasterPassword( + passwordStrength: number, + newPassword: string, + enforcedPolicyOptions: MasterPasswordPolicyOptions + ): boolean { + if (enforcedPolicyOptions == null) { + return true; + } + + if ( + enforcedPolicyOptions.minComplexity > 0 && + enforcedPolicyOptions.minComplexity > passwordStrength + ) { + return false; + } + + if ( + enforcedPolicyOptions.minLength > 0 && + enforcedPolicyOptions.minLength > newPassword.length + ) { + return false; + } + + if (enforcedPolicyOptions.requireUpper && newPassword.toLocaleLowerCase() === newPassword) { + return false; + } + + if (enforcedPolicyOptions.requireLower && newPassword.toLocaleUpperCase() === newPassword) { + return false; + } + + if (enforcedPolicyOptions.requireNumbers && !/[0-9]/.test(newPassword)) { + return false; + } + + // eslint-disable-next-line + if (enforcedPolicyOptions.requireSpecial && !/[!@#$%\^&*]/g.test(newPassword)) { + return false; + } + + return true; + } + + getResetPasswordPolicyOptions( + policies: Policy[], + orgId: string + ): [ResetPasswordPolicyOptions, boolean] { + const resetPasswordPolicyOptions = new ResetPasswordPolicyOptions(); + + if (policies == null || orgId == null) { + return [resetPasswordPolicyOptions, false]; + } + + const policy = policies.find( + (p) => p.organizationId === orgId && p.type === PolicyType.ResetPassword && p.enabled + ); + resetPasswordPolicyOptions.autoEnrollEnabled = policy?.data?.autoEnrollEnabled ?? false; + + return [resetPasswordPolicyOptions, policy?.enabled ?? false]; + } + + mapPoliciesFromToken(policiesResponse: ListResponse): Policy[] { + if (policiesResponse == null || policiesResponse.data == null) { + return null; + } + + const policiesData = policiesResponse.data.map((p) => new PolicyData(p)); + return policiesData.map((p) => new Policy(p)); + } + + async policyAppliesToUser( + policyType: PolicyType, + policyFilter?: (policy: Policy) => boolean, + userId?: string + ) { + const policies = await this.getAll(policyType, userId); + const organizations = await this.organizationService.getAll(userId); + let filteredPolicies; + + if (policyFilter != null) { + filteredPolicies = policies.filter((p) => p.enabled && policyFilter(p)); + } else { + filteredPolicies = policies.filter((p) => p.enabled); + } + + const policySet = new Set(filteredPolicies.map((p) => p.organizationId)); + + return organizations.some( + (o) => + o.enabled && + o.status >= OrganizationUserStatusType.Accepted && + o.usePolicies && + !this.isExcemptFromPolicies(o, policyType) && + policySet.has(o.id) + ); + } + + private isExcemptFromPolicies(organization: Organization, policyType: PolicyType) { + if (policyType === PolicyType.MaximumVaultTimeout) { + return organization.type === OrganizationUserType.Owner; + } + + return organization.isExemptFromPolicies; + } +} diff --git a/jslib/common/src/services/provider.service.ts b/jslib/common/src/services/provider.service.ts new file mode 100644 index 00000000..53f09542 --- /dev/null +++ b/jslib/common/src/services/provider.service.ts @@ -0,0 +1,34 @@ +import { ProviderService as ProviderServiceAbstraction } from "../abstractions/provider.service"; +import { StateService } from "../abstractions/state.service"; +import { ProviderData } from "../models/data/providerData"; +import { Provider } from "../models/domain/provider"; + +export class ProviderService implements ProviderServiceAbstraction { + constructor(private stateService: StateService) {} + + async get(id: string): Promise { + const providers = await this.stateService.getProviders(); + // eslint-disable-next-line + if (providers == null || !providers.hasOwnProperty(id)) { + return null; + } + + return new Provider(providers[id]); + } + + async getAll(): Promise { + const providers = await this.stateService.getProviders(); + const response: Provider[] = []; + for (const id in providers) { + // eslint-disable-next-line + if (providers.hasOwnProperty(id)) { + response.push(new Provider(providers[id])); + } + } + return response; + } + + async save(providers: { [id: string]: ProviderData }) { + await this.stateService.setProviders(providers); + } +} diff --git a/jslib/common/src/services/search.service.ts b/jslib/common/src/services/search.service.ts new file mode 100644 index 00000000..e1c54ceb --- /dev/null +++ b/jslib/common/src/services/search.service.ts @@ -0,0 +1,284 @@ +import * as lunr from "lunr"; + +import { CipherService } from "../abstractions/cipher.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { LogService } from "../abstractions/log.service"; +import { SearchService as SearchServiceAbstraction } from "../abstractions/search.service"; +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { CipherView } from "../models/view/cipherView"; +import { SendView } from "../models/view/sendView"; + +export class SearchService implements SearchServiceAbstraction { + indexedEntityId?: string = null; + private indexing = false; + private index: lunr.Index = null; + private searchableMinLength = 2; + + constructor( + private cipherService: CipherService, + private logService: LogService, + private i18nService: I18nService + ) { + if (["zh-CN", "zh-TW"].indexOf(i18nService.locale) !== -1) { + this.searchableMinLength = 1; + } + } + + clearIndex(): void { + this.indexedEntityId = null; + this.index = null; + } + + isSearchable(query: string): boolean { + const notSearchable = + query == null || + (this.index == null && query.length < this.searchableMinLength) || + (this.index != null && query.length < this.searchableMinLength && query.indexOf(">") !== 0); + return !notSearchable; + } + + async indexCiphers(indexedEntityId?: string, ciphers?: CipherView[]): Promise { + if (this.indexing) { + return; + } + + this.logService.time("search indexing"); + this.indexing = true; + this.indexedEntityId = indexedEntityId; + this.index = null; + const builder = new lunr.Builder(); + builder.ref("id"); + builder.field("shortid", { boost: 100, extractor: (c: CipherView) => c.id.substr(0, 8) }); + builder.field("name", { boost: 10 }); + builder.field("subtitle", { + boost: 5, + extractor: (c: CipherView) => { + if (c.subTitle != null && c.type === CipherType.Card) { + return c.subTitle.replace(/\*/g, ""); + } + return c.subTitle; + }, + }); + builder.field("notes"); + builder.field("login.username", { + extractor: (c: CipherView) => + c.type === CipherType.Login && c.login != null ? c.login.username : null, + }); + builder.field("login.uris", { boost: 2, extractor: (c: CipherView) => this.uriExtractor(c) }); + builder.field("fields", { extractor: (c: CipherView) => this.fieldExtractor(c, false) }); + builder.field("fields_joined", { extractor: (c: CipherView) => this.fieldExtractor(c, true) }); + builder.field("attachments", { + extractor: (c: CipherView) => this.attachmentExtractor(c, false), + }); + builder.field("attachments_joined", { + extractor: (c: CipherView) => this.attachmentExtractor(c, true), + }); + builder.field("organizationid", { extractor: (c: CipherView) => c.organizationId }); + ciphers = ciphers || (await this.cipherService.getAllDecrypted()); + ciphers.forEach((c) => builder.add(c)); + this.index = builder.build(); + + this.indexing = false; + + this.logService.timeEnd("search indexing"); + } + + async searchCiphers( + query: string, + filter: ((cipher: CipherView) => boolean) | ((cipher: CipherView) => boolean)[] = null, + ciphers: CipherView[] = null + ): Promise { + const results: CipherView[] = []; + if (query != null) { + query = query.trim().toLowerCase(); + } + if (query === "") { + query = null; + } + + if (ciphers == null) { + ciphers = await this.cipherService.getAllDecrypted(); + } + + if (filter != null && Array.isArray(filter) && filter.length > 0) { + ciphers = ciphers.filter((c) => filter.every((f) => f == null || f(c))); + } else if (filter != null) { + ciphers = ciphers.filter(filter as (cipher: CipherView) => boolean); + } + + if (!this.isSearchable(query)) { + return ciphers; + } + + if (this.indexing) { + await new Promise((r) => setTimeout(r, 250)); + if (this.indexing) { + await new Promise((r) => setTimeout(r, 500)); + } + } + + const index = this.getIndexForSearch(); + if (index == null) { + // Fall back to basic search if index is not available + return this.searchCiphersBasic(ciphers, query); + } + + const ciphersMap = new Map(); + ciphers.forEach((c) => ciphersMap.set(c.id, c)); + + let searchResults: lunr.Index.Result[] = null; + const isQueryString = query != null && query.length > 1 && query.indexOf(">") === 0; + if (isQueryString) { + try { + searchResults = index.search(query.substr(1).trim()); + } catch (e) { + this.logService.error(e); + } + } else { + const soWild = lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING; + searchResults = index.query((q) => { + lunr.tokenizer(query).forEach((token) => { + const t = token.toString(); + q.term(t, { fields: ["name"], wildcard: soWild }); + q.term(t, { fields: ["subtitle"], wildcard: soWild }); + q.term(t, { fields: ["login.uris"], wildcard: soWild }); + q.term(t, {}); + }); + }); + } + + if (searchResults != null) { + searchResults.forEach((r) => { + if (ciphersMap.has(r.ref)) { + results.push(ciphersMap.get(r.ref)); + } + }); + } + return results; + } + + searchCiphersBasic(ciphers: CipherView[], query: string, deleted = false) { + query = query.trim().toLowerCase(); + return ciphers.filter((c) => { + if (deleted !== c.isDeleted) { + return false; + } + if (c.name != null && c.name.toLowerCase().indexOf(query) > -1) { + return true; + } + if (query.length >= 8 && c.id.startsWith(query)) { + return true; + } + if (c.subTitle != null && c.subTitle.toLowerCase().indexOf(query) > -1) { + return true; + } + if (c.login && c.login.uri != null && c.login.uri.toLowerCase().indexOf(query) > -1) { + return true; + } + return false; + }); + } + + searchSends(sends: SendView[], query: string) { + query = query.trim().toLocaleLowerCase(); + + return sends.filter((s) => { + if (s.name != null && s.name.toLowerCase().indexOf(query) > -1) { + return true; + } + if ( + query.length >= 8 && + (s.id.startsWith(query) || + s.accessId.toLocaleLowerCase().startsWith(query) || + (s.file?.id != null && s.file.id.startsWith(query))) + ) { + return true; + } + if (s.notes != null && s.notes.toLowerCase().indexOf(query) > -1) { + return true; + } + if (s.text?.text != null && s.text.text.toLowerCase().indexOf(query) > -1) { + return true; + } + if (s.file?.fileName != null && s.file.fileName.toLowerCase().indexOf(query) > -1) { + return true; + } + }); + } + + getIndexForSearch(): lunr.Index { + return this.index; + } + + private fieldExtractor(c: CipherView, joined: boolean) { + if (!c.hasFields) { + return null; + } + let fields: string[] = []; + c.fields.forEach((f) => { + if (f.name != null) { + fields.push(f.name); + } + if (f.type === FieldType.Text && f.value != null) { + fields.push(f.value); + } + }); + fields = fields.filter((f) => f.trim() !== ""); + if (fields.length === 0) { + return null; + } + return joined ? fields.join(" ") : fields; + } + + private attachmentExtractor(c: CipherView, joined: boolean) { + if (!c.hasAttachments) { + return null; + } + let attachments: string[] = []; + c.attachments.forEach((a) => { + if (a != null && a.fileName != null) { + if (joined && a.fileName.indexOf(".") > -1) { + attachments.push(a.fileName.substr(0, a.fileName.lastIndexOf("."))); + } else { + attachments.push(a.fileName); + } + } + }); + attachments = attachments.filter((f) => f.trim() !== ""); + if (attachments.length === 0) { + return null; + } + return joined ? attachments.join(" ") : attachments; + } + + private uriExtractor(c: CipherView) { + if (c.type !== CipherType.Login || c.login == null || !c.login.hasUris) { + return null; + } + const uris: string[] = []; + c.login.uris.forEach((u) => { + if (u.uri == null || u.uri === "") { + return; + } + if (u.hostname != null) { + uris.push(u.hostname); + return; + } + let uri = u.uri; + if (u.match !== UriMatchType.RegularExpression) { + const protocolIndex = uri.indexOf("://"); + if (protocolIndex > -1) { + uri = uri.substr(protocolIndex + 3); + } + const queryIndex = uri.search(/\?|&|#/); + if (queryIndex > -1) { + uri = uri.substring(0, queryIndex); + } + } + uris.push(uri); + }); + return uris.length > 0 ? uris : null; + } +} diff --git a/jslib/common/src/services/send.service.ts b/jslib/common/src/services/send.service.ts new file mode 100644 index 00000000..75530db3 --- /dev/null +++ b/jslib/common/src/services/send.service.ts @@ -0,0 +1,297 @@ +import { ApiService } from "../abstractions/api.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { FileUploadService } from "../abstractions/fileUpload.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { SendService as SendServiceAbstraction } from "../abstractions/send.service"; +import { StateService } from "../abstractions/state.service"; +import { SEND_KDF_ITERATIONS } from "../enums/kdfType"; +import { SendType } from "../enums/sendType"; +import { Utils } from "../misc/utils"; +import { SendData } from "../models/data/sendData"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { Send } from "../models/domain/send"; +import { SendFile } from "../models/domain/sendFile"; +import { SendText } from "../models/domain/sendText"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { SendRequest } from "../models/request/sendRequest"; +import { ErrorResponse } from "../models/response/errorResponse"; +import { SendResponse } from "../models/response/sendResponse"; +import { SendView } from "../models/view/sendView"; + +export class SendService implements SendServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private apiService: ApiService, + private fileUploadService: FileUploadService, + private i18nService: I18nService, + private cryptoFunctionService: CryptoFunctionService, + private stateService: StateService + ) {} + + async clearCache(): Promise { + await this.stateService.setDecryptedSends(null); + } + + async encrypt( + model: SendView, + file: File | ArrayBuffer, + password: string, + key?: SymmetricCryptoKey + ): Promise<[Send, EncArrayBuffer]> { + let fileData: EncArrayBuffer = null; + const send = new Send(); + send.id = model.id; + send.type = model.type; + send.disabled = model.disabled; + send.hideEmail = model.hideEmail; + send.maxAccessCount = model.maxAccessCount; + if (model.key == null) { + model.key = await this.cryptoFunctionService.randomBytes(16); + model.cryptoKey = await this.cryptoService.makeSendKey(model.key); + } + if (password != null) { + const passwordHash = await this.cryptoFunctionService.pbkdf2( + password, + model.key, + "sha256", + SEND_KDF_ITERATIONS + ); + send.password = Utils.fromBufferToB64(passwordHash); + } + send.key = await this.cryptoService.encrypt(model.key, key); + send.name = await this.cryptoService.encrypt(model.name, model.cryptoKey); + send.notes = await this.cryptoService.encrypt(model.notes, model.cryptoKey); + if (send.type === SendType.Text) { + send.text = new SendText(); + send.text.text = await this.cryptoService.encrypt(model.text.text, model.cryptoKey); + send.text.hidden = model.text.hidden; + } else if (send.type === SendType.File) { + send.file = new SendFile(); + if (file != null) { + if (file instanceof ArrayBuffer) { + const [name, data] = await this.encryptFileData( + model.file.fileName, + file, + model.cryptoKey + ); + send.file.fileName = name; + fileData = data; + } else { + fileData = await this.parseFile(send, file, model.cryptoKey); + } + } + } + + return [send, fileData]; + } + + async get(id: string): Promise { + const sends = await this.stateService.getEncryptedSends(); + // eslint-disable-next-line + if (sends == null || !sends.hasOwnProperty(id)) { + return null; + } + + return new Send(sends[id]); + } + + async getAll(): Promise { + const sends = await this.stateService.getEncryptedSends(); + const response: Send[] = []; + for (const id in sends) { + // eslint-disable-next-line + if (sends.hasOwnProperty(id)) { + response.push(new Send(sends[id])); + } + } + return response; + } + + async getAllDecrypted(): Promise { + let decSends = await this.stateService.getDecryptedSends(); + if (decSends != null) { + return decSends; + } + + decSends = []; + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + throw new Error("No key."); + } + + const promises: Promise[] = []; + const sends = await this.getAll(); + sends.forEach((send) => { + promises.push(send.decrypt().then((f) => decSends.push(f))); + }); + + await Promise.all(promises); + decSends.sort(Utils.getSortFunction(this.i18nService, "name")); + + await this.stateService.setDecryptedSends(decSends); + return decSends; + } + + async saveWithServer(sendData: [Send, EncArrayBuffer]): Promise { + const request = new SendRequest(sendData[0], sendData[1]?.buffer.byteLength); + let response: SendResponse; + if (sendData[0].id == null) { + if (sendData[0].type === SendType.Text) { + response = await this.apiService.postSend(request); + } else { + try { + const uploadDataResponse = await this.apiService.postFileTypeSend(request); + response = uploadDataResponse.sendResponse; + + await this.fileUploadService.uploadSendFile( + uploadDataResponse, + sendData[0].file.fileName, + sendData[1] + ); + } catch (e) { + if (e instanceof ErrorResponse && (e as ErrorResponse).statusCode === 404) { + response = await this.legacyServerSendFileUpload(sendData, request); + } else if (e instanceof ErrorResponse) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } else { + throw e; + } + } + } + sendData[0].id = response.id; + sendData[0].accessId = response.accessId; + } else { + response = await this.apiService.putSend(sendData[0].id, request); + } + + const userId = await this.stateService.getUserId(); + const data = new SendData(response, userId); + await this.upsert(data); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async legacyServerSendFileUpload( + sendData: [Send, EncArrayBuffer], + request: SendRequest + ): Promise { + const fd = new FormData(); + try { + const blob = new Blob([sendData[1].buffer], { type: "application/octet-stream" }); + fd.append("model", JSON.stringify(request)); + fd.append("data", blob, sendData[0].file.fileName.encryptedString); + } catch (e) { + if (Utils.isNode && !Utils.isBrowser) { + fd.append("model", JSON.stringify(request)); + fd.append( + "data", + Buffer.from(sendData[1].buffer) as any, + { + filepath: sendData[0].file.fileName.encryptedString, + contentType: "application/octet-stream", + } as any + ); + } else { + throw e; + } + } + return await this.apiService.postSendFileLegacy(fd); + } + + async upsert(send: SendData | SendData[]): Promise { + let sends = await this.stateService.getEncryptedSends(); + if (sends == null) { + sends = {}; + } + + if (send instanceof SendData) { + const s = send as SendData; + sends[s.id] = s; + } else { + (send as SendData[]).forEach((s) => { + sends[s.id] = s; + }); + } + + await this.replace(sends); + } + + async replace(sends: { [id: string]: SendData }): Promise { + await this.stateService.setDecryptedSends(null); + await this.stateService.setEncryptedSends(sends); + } + + async clear(): Promise { + await this.stateService.setDecryptedSends(null); + await this.stateService.setEncryptedSends(null); + } + + async delete(id: string | string[]): Promise { + const sends = await this.stateService.getEncryptedSends(); + if (sends == null) { + return; + } + + if (typeof id === "string") { + if (sends[id] == null) { + return; + } + delete sends[id]; + } else { + (id as string[]).forEach((i) => { + delete sends[i]; + }); + } + + await this.replace(sends); + } + + async deleteWithServer(id: string): Promise { + await this.apiService.deleteSend(id); + await this.delete(id); + } + + async removePasswordWithServer(id: string): Promise { + const response = await this.apiService.putSendRemovePassword(id); + const userId = await this.stateService.getUserId(); + const data = new SendData(response, userId); + await this.upsert(data); + } + + private parseFile(send: Send, file: File, key: SymmetricCryptoKey): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsArrayBuffer(file); + reader.onload = async (evt) => { + try { + const [name, data] = await this.encryptFileData( + file.name, + evt.target.result as ArrayBuffer, + key + ); + send.file.fileName = name; + resolve(data); + } catch (e) { + reject(e); + } + }; + reader.onerror = () => { + reject("Error reading file."); + }; + }); + } + + private async encryptFileData( + fileName: string, + data: ArrayBuffer, + key: SymmetricCryptoKey + ): Promise<[EncString, EncArrayBuffer]> { + const encFileName = await this.cryptoService.encrypt(fileName, key); + const encFileData = await this.cryptoService.encryptToBytes(data, key); + return [encFileName, encFileData]; + } +} diff --git a/jslib/common/src/services/settings.service.ts b/jslib/common/src/services/settings.service.ts new file mode 100644 index 00000000..7f5131b0 --- /dev/null +++ b/jslib/common/src/services/settings.service.ts @@ -0,0 +1,56 @@ +import { SettingsService as SettingsServiceAbstraction } from "../abstractions/settings.service"; +import { StateService } from "../abstractions/state.service"; + +const Keys = { + settingsPrefix: "settings_", + equivalentDomains: "equivalentDomains", +}; + +export class SettingsService implements SettingsServiceAbstraction { + constructor(private stateService: StateService) {} + + async clearCache(): Promise { + await this.stateService.setSettings(null); + } + + getEquivalentDomains(): Promise { + return this.getSettingsKey(Keys.equivalentDomains); + } + + async setEquivalentDomains(equivalentDomains: string[][]): Promise { + await this.setSettingsKey(Keys.equivalentDomains, equivalentDomains); + } + + async clear(userId?: string): Promise { + await this.stateService.setSettings(null, { userId: userId }); + } + + // Helpers + + private async getSettings(): Promise { + const settings = await this.stateService.getSettings(); + if (settings == null) { + // eslint-disable-next-line + const userId = await this.stateService.getUserId(); + } + return settings; + } + + private async getSettingsKey(key: string): Promise { + const settings = await this.getSettings(); + if (settings != null && settings[key]) { + return settings[key]; + } + return null; + } + + private async setSettingsKey(key: string, value: any): Promise { + let settings = await this.getSettings(); + if (!settings) { + settings = {}; + } + + settings[key] = value; + await this.stateService.setSettings(settings); + } +} diff --git a/jslib/common/src/services/state.service.ts b/jslib/common/src/services/state.service.ts new file mode 100644 index 00000000..39a08f50 --- /dev/null +++ b/jslib/common/src/services/state.service.ts @@ -0,0 +1,2536 @@ +import { BehaviorSubject } from "rxjs"; + +import { LogService } from "../abstractions/log.service"; +import { StateService as StateServiceAbstraction } from "../abstractions/state.service"; +import { StateMigrationService } from "../abstractions/stateMigration.service"; +import { StorageService } from "../abstractions/storage.service"; +import { HtmlStorageLocation } from "../enums/htmlStorageLocation"; +import { KdfType } from "../enums/kdfType"; +import { StorageLocation } from "../enums/storageLocation"; +import { ThemeType } from "../enums/themeType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { StateFactory } from "../factories/stateFactory"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { EventData } from "../models/data/eventData"; +import { FolderData } from "../models/data/folderData"; +import { OrganizationData } from "../models/data/organizationData"; +import { PolicyData } from "../models/data/policyData"; +import { ProviderData } from "../models/data/providerData"; +import { SendData } from "../models/data/sendData"; +import { Account, AccountData } from "../models/domain/account"; +import { EncString } from "../models/domain/encString"; +import { EnvironmentUrls } from "../models/domain/environmentUrls"; +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { GlobalState } from "../models/domain/globalState"; +import { Policy } from "../models/domain/policy"; +import { State } from "../models/domain/state"; +import { StorageOptions } from "../models/domain/storageOptions"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { WindowState } from "../models/domain/windowState"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { FolderView } from "../models/view/folderView"; +import { SendView } from "../models/view/sendView"; + +const keys = { + global: "global", + authenticatedAccounts: "authenticatedAccounts", + activeUserId: "activeUserId", + tempAccountSettings: "tempAccountSettings", // used to hold account specific settings (i.e clear clipboard) between initial migration and first account authentication + accountActivity: "accountActivity", +}; + +const partialKeys = { + autoKey: "_masterkey_auto", + biometricKey: "_masterkey_biometric", + masterKey: "_masterkey", +}; + +export class StateService< + TGlobalState extends GlobalState = GlobalState, + TAccount extends Account = Account +> implements StateServiceAbstraction +{ + accounts = new BehaviorSubject<{ [userId: string]: TAccount }>({}); + activeAccount = new BehaviorSubject(null); + + protected state: State = new State( + this.createGlobals() + ); + + private hasBeenInited = false; + + private accountDiskCache: Map; + + constructor( + protected storageService: StorageService, + protected secureStorageService: StorageService, + protected logService: LogService, + protected stateMigrationService: StateMigrationService, + protected stateFactory: StateFactory, + protected useAccountCache: boolean = true + ) { + this.accountDiskCache = new Map(); + } + + async init(): Promise { + if (this.hasBeenInited) { + return; + } + + if (await this.stateMigrationService.needsMigration()) { + await this.stateMigrationService.migrate(); + } + + await this.initAccountState(); + this.hasBeenInited = true; + } + + async initAccountState() { + this.state.authenticatedAccounts = + (await this.storageService.get(keys.authenticatedAccounts)) ?? []; + for (const i in this.state.authenticatedAccounts) { + if (i != null) { + await this.syncAccountFromDisk(this.state.authenticatedAccounts[i]); + } + } + const storedActiveUser = await this.storageService.get(keys.activeUserId); + if (storedActiveUser != null) { + this.state.activeUserId = storedActiveUser; + } + await this.pushAccounts(); + this.activeAccount.next(this.state.activeUserId); + } + + async syncAccountFromDisk(userId: string) { + if (userId == null) { + return; + } + this.state.accounts[userId] = this.createAccount(); + const diskAccount = await this.getAccountFromDisk({ userId: userId }); + this.state.accounts[userId].profile = diskAccount.profile; + } + + async addAccount(account: TAccount) { + account = await this.setAccountEnvironmentUrls(account); + this.state.authenticatedAccounts.push(account.profile.userId); + await this.storageService.save(keys.authenticatedAccounts, this.state.authenticatedAccounts); + this.state.accounts[account.profile.userId] = account; + await this.scaffoldNewAccountStorage(account); + await this.setLastActive(new Date().getTime(), { userId: account.profile.userId }); + await this.setActiveUser(account.profile.userId); + this.activeAccount.next(account.profile.userId); + } + + async setActiveUser(userId: string): Promise { + this.clearDecryptedDataForActiveUser(); + this.state.activeUserId = userId; + await this.storageService.save(keys.activeUserId, userId); + this.activeAccount.next(this.state.activeUserId); + await this.pushAccounts(); + } + + async clean(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, this.defaultInMemoryOptions); + await this.deAuthenticateAccount(options.userId); + if (options.userId === this.state.activeUserId) { + await this.dynamicallySetActiveUser(); + } + + await this.removeAccountFromDisk(options?.userId); + this.removeAccountFromMemory(options?.userId); + await this.pushAccounts(); + } + + async getAccessToken(options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + return (await this.getAccount(options))?.tokens?.accessToken; + } + + async setAccessToken(value: string, options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + const account = await this.getAccount(options); + account.tokens.accessToken = value; + await this.saveAccount(account, options); + } + + async getAddEditCipherInfo(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.addEditCipherInfo; + } + + async setAddEditCipherInfo(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.addEditCipherInfo = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getAlwaysShowDock(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.alwaysShowDock ?? false + ); + } + + async setAlwaysShowDock(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.alwaysShowDock = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getApiKeyClientId(options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + return (await this.getAccount(options))?.profile?.apiKeyClientId; + } + + async setApiKeyClientId(value: string, options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + const account = await this.getAccount(options); + account.profile.apiKeyClientId = value; + await this.saveAccount(account, options); + } + + async getApiKeyClientSecret(options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + return (await this.getAccount(options))?.keys?.apiKeyClientSecret; + } + + async setApiKeyClientSecret(value: string, options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + const account = await this.getAccount(options); + account.keys.apiKeyClientSecret = value; + await this.saveAccount(account, options); + } + + async getAutoConfirmFingerPrints(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.autoConfirmFingerPrints ?? false + ); + } + + async setAutoConfirmFingerprints(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.autoConfirmFingerPrints = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getAutoFillOnPageLoadDefault(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.autoFillOnPageLoadDefault ?? true + ); + } + + async setAutoFillOnPageLoadDefault(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.autoFillOnPageLoadDefault = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getBiometricAwaitingAcceptance(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.biometricAwaitingAcceptance ?? false + ); + } + + async setBiometricAwaitingAcceptance(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.biometricAwaitingAcceptance = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getBiometricFingerprintValidated(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.biometricFingerprintValidated ?? false + ); + } + + async setBiometricFingerprintValidated(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.biometricFingerprintValidated = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getBiometricLocked(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))?.settings + ?.biometricLocked ?? false + ); + } + + async setBiometricLocked(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.settings.biometricLocked = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getBiometricText(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.biometricText; + } + + async setBiometricText(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.biometricText = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getBiometricUnlock(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.biometricUnlock ?? false + ); + } + + async setBiometricUnlock(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.biometricUnlock = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getCanAccessPremium(options?: StorageOptions): Promise { + if (!(await this.getIsAuthenticated(options))) { + return false; + } + + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + if (account.profile.hasPremiumPersonally) { + return true; + } + + const organizations = await this.getOrganizations(options); + if (organizations == null) { + return false; + } + + for (const id of Object.keys(organizations)) { + const o = organizations[id]; + if (o.enabled && o.usersGetPremium && !o.isProviderUser) { + return true; + } + } + + return false; + } + + async getClearClipboard(options?: StorageOptions): Promise { + return ( + ( + await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ) + )?.settings?.clearClipboard ?? null + ); + } + + async setClearClipboard(value: number, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.clearClipboard = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getCollapsedGroupings(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.settings?.collapsedGroupings; + } + + async setCollapsedGroupings(value: string[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.collapsedGroupings = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getConvertAccountToKeyConnector(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.convertAccountToKeyConnector; + } + + async setConvertAccountToKeyConnector(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.convertAccountToKeyConnector = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getCryptoMasterKey(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.cryptoMasterKey; + } + + async setCryptoMasterKey(value: SymmetricCryptoKey, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.cryptoMasterKey = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getCryptoMasterKeyAuto(options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "auto" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return null; + } + return await this.secureStorageService.get(`${options.userId}${partialKeys.autoKey}`, options); + } + + async setCryptoMasterKeyAuto(value: string, options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "auto" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return; + } + await this.saveSecureStorageKey(partialKeys.autoKey, value, options); + } + + async getCryptoMasterKeyB64(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultSecureStorageOptions()); + if (options?.userId == null) { + return null; + } + return await this.secureStorageService.get( + `${options?.userId}${partialKeys.masterKey}`, + options + ); + } + + async setCryptoMasterKeyB64(value: string, options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultSecureStorageOptions()); + if (options?.userId == null) { + return; + } + await this.saveSecureStorageKey(partialKeys.masterKey, value, options); + } + + async getCryptoMasterKeyBiometric(options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "biometric" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return null; + } + return await this.secureStorageService.get( + `${options.userId}${partialKeys.biometricKey}`, + options + ); + } + + async hasCryptoMasterKeyBiometric(options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "biometric" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return false; + } + return await this.secureStorageService.has( + `${options.userId}${partialKeys.biometricKey}`, + options + ); + } + + async setCryptoMasterKeyBiometric(value: string, options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "biometric" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return; + } + await this.saveSecureStorageKey(partialKeys.biometricKey, value, options); + } + + async getDecodedToken(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.tokens?.decodedToken; + } + + async setDecodedToken(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.tokens.decodedToken = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedCiphers(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.ciphers?.decrypted; + } + + async setDecryptedCiphers(value: CipherView[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.ciphers.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedCollections(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.collections?.decrypted; + } + + async setDecryptedCollections(value: CollectionView[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.collections.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedCryptoSymmetricKey(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.cryptoSymmetricKey?.decrypted; + } + + async setDecryptedCryptoSymmetricKey( + value: SymmetricCryptoKey, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.cryptoSymmetricKey.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedFolders(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.folders?.decrypted; + } + + async setDecryptedFolders(value: FolderView[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.folders.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedOrganizationKeys( + options?: StorageOptions + ): Promise> { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.organizationKeys?.decrypted; + } + + async setDecryptedOrganizationKeys( + value: Map, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.organizationKeys.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedPasswordGenerationHistory( + options?: StorageOptions + ): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.passwordGenerationHistory?.decrypted; + } + + async setDecryptedPasswordGenerationHistory( + value: GeneratedPasswordHistory[], + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.passwordGenerationHistory.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedPinProtected(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.settings?.pinProtected?.decrypted; + } + + async setDecryptedPinProtected(value: EncString, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.settings.pinProtected.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedPolicies(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.policies?.decrypted; + } + + async setDecryptedPolicies(value: Policy[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.policies.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedPrivateKey(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.privateKey?.decrypted; + } + + async setDecryptedPrivateKey(value: ArrayBuffer, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.privateKey.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedProviderKeys( + options?: StorageOptions + ): Promise> { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.providerKeys?.decrypted; + } + + async setDecryptedProviderKeys( + value: Map, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.providerKeys.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedSends(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.sends?.decrypted; + } + + async setDecryptedSends(value: SendView[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.sends.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDefaultUriMatch(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.defaultUriMatch; + } + + async setDefaultUriMatch(value: UriMatchType, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.defaultUriMatch = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableAddLoginNotification(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableAddLoginNotification ?? false + ); + } + + async setDisableAddLoginNotification(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableAddLoginNotification = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableAutoBiometricsPrompt(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableAutoBiometricsPrompt ?? false + ); + } + + async setDisableAutoBiometricsPrompt(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableAutoBiometricsPrompt = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableAutoTotpCopy(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableAutoTotpCopy ?? false + ); + } + + async setDisableAutoTotpCopy(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableAutoTotpCopy = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableBadgeCounter(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableBadgeCounter ?? false + ); + } + + async setDisableBadgeCounter(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableBadgeCounter = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableChangedPasswordNotification(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableChangedPasswordNotification ?? false + ); + } + + async setDisableChangedPasswordNotification( + value: boolean, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableChangedPasswordNotification = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableContextMenuItem(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableContextMenuItem ?? false + ); + } + + async setDisableContextMenuItem(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableContextMenuItem = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableFavicon(options?: StorageOptions): Promise { + return ( + ( + await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ) + )?.disableFavicon ?? false + ); + } + + async setDisableFavicon(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.disableFavicon = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getDisableGa(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableGa ?? false + ); + } + + async setDisableGa(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableGa = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDontShowCardsCurrentTab(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.dontShowCardsCurrentTab ?? false + ); + } + + async setDontShowCardsCurrentTab(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.dontShowCardsCurrentTab = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDontShowIdentitiesCurrentTab(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.dontShowIdentitiesCurrentTab ?? false + ); + } + + async setDontShowIdentitiesCurrentTab(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.dontShowIdentitiesCurrentTab = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEmail(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.profile?.email; + } + + async setEmail(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.profile.email = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getEmailVerified(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.profile.emailVerified ?? false + ); + } + + async setEmailVerified(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.emailVerified = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableAlwaysOnTop(options?: StorageOptions): Promise { + const accountPreference = ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.enableAlwaysOnTop; + const globalPreference = ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.enableAlwaysOnTop; + return accountPreference ?? globalPreference ?? false; + } + + async setEnableAlwaysOnTop(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.enableAlwaysOnTop = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableAlwaysOnTop = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableAutoFillOnPageLoad(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.enableAutoFillOnPageLoad ?? false + ); + } + + async setEnableAutoFillOnPageLoad(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.enableAutoFillOnPageLoad = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableBiometric(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableBiometrics ?? false + ); + } + + async setEnableBiometric(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableBiometrics = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableBrowserIntegration(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableBrowserIntegration ?? false + ); + } + + async setEnableBrowserIntegration(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableBrowserIntegration = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableBrowserIntegrationFingerprint(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableBrowserIntegrationFingerprint ?? false + ); + } + + async setEnableBrowserIntegrationFingerprint( + value: boolean, + options?: StorageOptions + ): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableBrowserIntegrationFingerprint = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableCloseToTray(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableCloseToTray ?? false + ); + } + + async setEnableCloseToTray(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableCloseToTray = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableFullWidth(options?: StorageOptions): Promise { + return ( + ( + await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ) + )?.settings?.enableFullWidth ?? false + ); + } + + async setEnableFullWidth(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.enableFullWidth = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getEnableGravitars(options?: StorageOptions): Promise { + return ( + ( + await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ) + )?.settings?.enableGravitars ?? false + ); + } + + async setEnableGravitars(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.enableGravitars = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getEnableMinimizeToTray(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableMinimizeToTray ?? false + ); + } + + async setEnableMinimizeToTray(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableMinimizeToTray = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableStartToTray(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableStartToTray ?? false + ); + } + + async setEnableStartToTray(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableStartToTray = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableTray(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableTray ?? false + ); + } + + async setEnableTray(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableTray = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedCiphers(options?: StorageOptions): Promise<{ [id: string]: CipherData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.data?.ciphers?.encrypted; + } + + async setEncryptedCiphers( + value: { [id: string]: CipherData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.data.ciphers.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getEncryptedCollections( + options?: StorageOptions + ): Promise<{ [id: string]: CollectionData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.data?.collections?.encrypted; + } + + async setEncryptedCollections( + value: { [id: string]: CollectionData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.data.collections.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getEncryptedCryptoSymmetricKey(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys.cryptoSymmetricKey.encrypted; + } + + async setEncryptedCryptoSymmetricKey(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.cryptoSymmetricKey.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedFolders(options?: StorageOptions): Promise<{ [id: string]: FolderData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.data?.folders?.encrypted; + } + + async setEncryptedFolders( + value: { [id: string]: FolderData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.data.folders.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getEncryptedOrganizationKeys(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys?.organizationKeys.encrypted; + } + + async setEncryptedOrganizationKeys( + value: Map, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.organizationKeys.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedPasswordGenerationHistory( + options?: StorageOptions + ): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.passwordGenerationHistory?.encrypted; + } + + async setEncryptedPasswordGenerationHistory( + value: GeneratedPasswordHistory[], + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.passwordGenerationHistory.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedPinProtected(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.pinProtected?.encrypted; + } + + async setEncryptedPinProtected(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.pinProtected.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedPolicies(options?: StorageOptions): Promise<{ [id: string]: PolicyData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.policies?.encrypted; + } + + async setEncryptedPolicies( + value: { [id: string]: PolicyData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.policies.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedPrivateKey(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys?.privateKey?.encrypted; + } + + async setEncryptedPrivateKey(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.privateKey.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedProviderKeys(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys?.providerKeys?.encrypted; + } + + async setEncryptedProviderKeys(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.providerKeys.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedSends(options?: StorageOptions): Promise<{ [id: string]: SendData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.data?.sends.encrypted; + } + + async setEncryptedSends( + value: { [id: string]: SendData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.data.sends.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getEntityId(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.profile?.entityId; + } + + async setEntityId(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.profile.entityId = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getEntityType(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.profile?.entityType; + } + + async setEntityType(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.profile.entityType = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getEnvironmentUrls(options?: StorageOptions): Promise { + if (this.state.activeUserId == null) { + return await this.getGlobalEnvironmentUrls(options); + } + options = this.reconcileOptions(options, await this.defaultOnDiskOptions()); + return (await this.getAccount(options))?.settings?.environmentUrls ?? new EnvironmentUrls(); + } + + async setEnvironmentUrls(value: EnvironmentUrls, options?: StorageOptions): Promise { + // Global values are set on each change and the current global settings are passed to any newly authed accounts. + // This is to allow setting environement values before an account is active, while still allowing individual accounts to have their own environments. + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.environmentUrls = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEquivalentDomains(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.equivalentDomains; + } + + async setEquivalentDomains(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.equivalentDomains = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEventCollection(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.eventCollection; + } + + async setEventCollection(value: EventData[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.eventCollection = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEverBeenUnlocked(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))?.profile + ?.everBeenUnlocked ?? false + ); + } + + async setEverBeenUnlocked(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.profile.everBeenUnlocked = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getForcePasswordReset(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))?.profile + ?.forcePasswordReset ?? false + ); + } + + async setForcePasswordReset(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.profile.forcePasswordReset = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getInstalledVersion(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.installedVersion; + } + + async setInstalledVersion(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.installedVersion = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getIsAuthenticated(options?: StorageOptions): Promise { + return (await this.getAccessToken(options)) != null && (await this.getUserId(options)) != null; + } + + async getKdfIterations(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.kdfIterations; + } + + async setKdfIterations(value: number, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.kdfIterations = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getKdfType(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.kdfType; + } + + async setKdfType(value: KdfType, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.kdfType = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getKeyHash(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.keyHash; + } + + async setKeyHash(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.keyHash = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getLastActive(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultOnDiskOptions()); + + const accountActivity = await this.storageService.get<{ [userId: string]: number }>( + keys.accountActivity, + options + ); + + if (accountActivity == null || Object.keys(accountActivity).length < 1) { + return null; + } + + return accountActivity[options.userId]; + } + + async setLastActive(value: number, options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultOnDiskOptions()); + if (options.userId == null) { + return; + } + const accountActivity = + (await this.storageService.get<{ [userId: string]: number }>( + keys.accountActivity, + options + )) ?? {}; + accountActivity[options.userId] = value; + await this.storageService.save(keys.accountActivity, accountActivity, options); + } + + async getLastSync(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.profile?.lastSync; + } + + async setLastSync(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.profile.lastSync = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getLegacyEtmKey(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys?.legacyEtmKey; + } + + async setLegacyEtmKey(value: SymmetricCryptoKey, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.legacyEtmKey = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getLocalData(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.data?.localData; + } + async setLocalData(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.data.localData = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getLocale(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.locale; + } + + async setLocale(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.locale = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getLoginRedirect(options?: StorageOptions): Promise { + return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.loginRedirect; + } + + async setLoginRedirect(value: any, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + globals.loginRedirect = value; + await this.saveGlobals(globals, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getMainWindowSize(options?: StorageOptions): Promise { + return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.mainWindowSize; + } + + async setMainWindowSize(value: number, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + globals.mainWindowSize = value; + await this.saveGlobals(globals, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getMinimizeOnCopyToClipboard(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.minimizeOnCopyToClipboard ?? false + ); + } + + async setMinimizeOnCopyToClipboard(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.minimizeOnCopyToClipboard = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getNeverDomains(options?: StorageOptions): Promise<{ [id: string]: any }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.neverDomains; + } + + async setNeverDomains(value: { [id: string]: any }, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.neverDomains = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getNoAutoPromptBiometrics(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.noAutoPromptBiometrics ?? false + ); + } + + async setNoAutoPromptBiometrics(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.noAutoPromptBiometrics = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getNoAutoPromptBiometricsText(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.noAutoPromptBiometricsText; + } + + async setNoAutoPromptBiometricsText(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.noAutoPromptBiometricsText = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getOpenAtLogin(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.openAtLogin ?? false + ); + } + + async setOpenAtLogin(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.openAtLogin = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getOrganizationInvitation(options?: StorageOptions): Promise { + return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.organizationInvitation; + } + + async setOrganizationInvitation(value: any, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + globals.organizationInvitation = value; + await this.saveGlobals(globals, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getOrganizations(options?: StorageOptions): Promise<{ [id: string]: OrganizationData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.organizations; + } + + async setOrganizations( + value: { [id: string]: OrganizationData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.organizations = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getPasswordGenerationOptions(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.passwordGenerationOptions; + } + + async setPasswordGenerationOptions(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.passwordGenerationOptions = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getUsernameGenerationOptions(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.usernameGenerationOptions; + } + + async setUsernameGenerationOptions(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.usernameGenerationOptions = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getGeneratorOptions(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.generatorOptions; + } + + async setGeneratorOptions(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.generatorOptions = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getProtectedPin(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.protectedPin; + } + + async setProtectedPin(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.protectedPin = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getProviders(options?: StorageOptions): Promise<{ [id: string]: ProviderData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.providers; + } + + async setProviders( + value: { [id: string]: ProviderData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.providers = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getPublicKey(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.publicKey; + } + + async setPublicKey(value: ArrayBuffer, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.publicKey = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getRefreshToken(options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + return (await this.getAccount(options))?.tokens?.refreshToken; + } + + async setRefreshToken(value: string, options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + const account = await this.getAccount(options); + account.tokens.refreshToken = value; + await this.saveAccount(account, options); + } + + async getRememberedEmail(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.rememberedEmail; + } + + async setRememberedEmail(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.rememberedEmail = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getSecurityStamp(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.tokens?.securityStamp; + } + + async setSecurityStamp(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.tokens.securityStamp = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getSettings(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.settings?.settings; + } + + async setSettings(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.settings.settings = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getSsoCodeVerifier(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.ssoCodeVerifier; + } + + async setSsoCodeVerifier(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.ssoCodeVerifier = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getSsoOrgIdentifier(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.ssoOrganizationIdentifier; + } + + async setSsoOrganizationIdentifier(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.ssoOrganizationIdentifier = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getSsoState(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.ssoState; + } + + async setSsoState(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.ssoState = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getTheme(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.theme; + } + + async setTheme(value: ThemeType, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.theme = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getTwoFactorToken(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.twoFactorToken; + } + + async setTwoFactorToken(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.twoFactorToken = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getUserId(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.userId; + } + + async getUsesKeyConnector(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.usesKeyConnector; + } + + async setUsesKeyConnector(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.usesKeyConnector = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getVaultTimeout(options?: StorageOptions): Promise { + const accountVaultTimeout = ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.settings?.vaultTimeout; + return accountVaultTimeout; + } + + async setVaultTimeout(value: number, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.vaultTimeout = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getVaultTimeoutAction(options?: StorageOptions): Promise { + const accountVaultTimeoutAction = ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.settings?.vaultTimeoutAction; + const globalVaultTimeoutAction = ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.vaultTimeoutAction; + return accountVaultTimeoutAction ?? globalVaultTimeoutAction; + } + + async setVaultTimeoutAction(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.vaultTimeoutAction = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getStateVersion(): Promise { + return (await this.getGlobals(await this.defaultOnDiskLocalOptions())).stateVersion ?? 1; + } + + async setStateVersion(value: number): Promise { + const globals = await this.getGlobals(await this.defaultOnDiskOptions()); + globals.stateVersion = value; + await this.saveGlobals(globals, await this.defaultOnDiskOptions()); + } + + async getWindow(): Promise { + const globals = await this.getGlobals(await this.defaultOnDiskOptions()); + return globals?.window != null && Object.keys(globals.window).length > 0 + ? globals.window + : new WindowState(); + } + + async setWindow(value: WindowState, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.window = value; + return await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + protected async getGlobals(options: StorageOptions): Promise { + let globals: TGlobalState; + if (this.useMemory(options.storageLocation)) { + globals = this.getGlobalsFromMemory(); + } + + if (this.useDisk && globals == null) { + globals = await this.getGlobalsFromDisk(options); + } + + return globals ?? this.createGlobals(); + } + + protected async saveGlobals(globals: TGlobalState, options: StorageOptions) { + return this.useMemory(options.storageLocation) + ? this.saveGlobalsToMemory(globals) + : await this.saveGlobalsToDisk(globals, options); + } + + protected getGlobalsFromMemory(): TGlobalState { + return this.state.globals; + } + + protected async getGlobalsFromDisk(options: StorageOptions): Promise { + return await this.storageService.get(keys.global, options); + } + + protected saveGlobalsToMemory(globals: TGlobalState): void { + this.state.globals = globals; + } + + protected async saveGlobalsToDisk(globals: TGlobalState, options: StorageOptions): Promise { + if (options.useSecureStorage) { + await this.secureStorageService.save(keys.global, globals, options); + } else { + await this.storageService.save(keys.global, globals, options); + } + } + + protected async getAccount(options: StorageOptions): Promise { + try { + let account: TAccount; + if (this.useMemory(options.storageLocation)) { + account = this.getAccountFromMemory(options); + } + + if (this.useDisk(options.storageLocation) && account == null) { + account = await this.getAccountFromDisk(options); + } + + return account; + } catch (e) { + this.logService.error(e); + } + } + + protected getAccountFromMemory(options: StorageOptions): TAccount { + if (this.state.accounts == null) { + return null; + } + return this.state.accounts[this.getUserIdFromMemory(options)]; + } + + protected getUserIdFromMemory(options: StorageOptions): string { + return options?.userId != null + ? this.state.accounts[options.userId]?.profile?.userId + : this.state.activeUserId; + } + + protected async getAccountFromDisk(options: StorageOptions): Promise { + if (options?.userId == null && this.state.activeUserId == null) { + return null; + } + + if (this.useAccountCache) { + const cachedAccount = this.accountDiskCache.get(options.userId); + if (cachedAccount != null) { + return cachedAccount; + } + } + + const account = options?.useSecureStorage + ? (await this.secureStorageService.get(options.userId, options)) ?? + (await this.storageService.get( + options.userId, + this.reconcileOptions(options, { htmlStorageLocation: HtmlStorageLocation.Local }) + )) + : await this.storageService.get(options.userId, options); + + if (this.useAccountCache) { + this.accountDiskCache.set(options.userId, account); + } + return account; + } + + protected useMemory(storageLocation: StorageLocation) { + return storageLocation === StorageLocation.Memory || storageLocation === StorageLocation.Both; + } + + protected useDisk(storageLocation: StorageLocation) { + return storageLocation === StorageLocation.Disk || storageLocation === StorageLocation.Both; + } + + protected async saveAccount( + account: TAccount, + options: StorageOptions = { + storageLocation: StorageLocation.Both, + useSecureStorage: false, + } + ) { + return this.useMemory(options.storageLocation) + ? await this.saveAccountToMemory(account) + : await this.saveAccountToDisk(account, options); + } + + protected async saveAccountToDisk(account: TAccount, options: StorageOptions): Promise { + const storageLocation = options.useSecureStorage + ? this.secureStorageService + : this.storageService; + + await storageLocation.save(`${options.userId}`, account, options); + + if (this.useAccountCache) { + this.accountDiskCache.delete(options.userId); + } + } + + protected async saveAccountToMemory(account: TAccount): Promise { + if (this.getAccountFromMemory({ userId: account.profile.userId }) !== null) { + this.state.accounts[account.profile.userId] = account; + } + await this.pushAccounts(); + } + + protected async scaffoldNewAccountStorage(account: TAccount): Promise { + // We don't want to manipulate the referenced in memory account + const deepClone = JSON.parse(JSON.stringify(account)); + await this.scaffoldNewAccountLocalStorage(deepClone); + await this.scaffoldNewAccountSessionStorage(deepClone); + await this.scaffoldNewAccountMemoryStorage(deepClone); + } + + // TODO: There is a tech debt item for splitting up these methods - only Web uses multiple storage locations in its storageService. + // For now these methods exist with some redundancy to facilitate this special web requirement. + protected async scaffoldNewAccountLocalStorage(account: TAccount): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions( + { userId: account.profile.userId }, + await this.defaultOnDiskLocalOptions() + ) + ); + // EnvironmentUrls are set before authenticating and should override whatever is stored from any previous session + const environmentUrls = account.settings.environmentUrls; + if (storedAccount?.settings != null) { + account.settings = storedAccount.settings; + } else if (await this.storageService.has(keys.tempAccountSettings)) { + account.settings = await this.storageService.get(keys.tempAccountSettings); + await this.storageService.remove(keys.tempAccountSettings); + } + account.settings.environmentUrls = environmentUrls; + if (account.settings.vaultTimeoutAction === "logOut" && account.settings.vaultTimeout != null) { + account.tokens.accessToken = null; + account.tokens.refreshToken = null; + account.profile.apiKeyClientId = null; + account.keys.apiKeyClientSecret = null; + } + await this.saveAccount( + account, + this.reconcileOptions( + { userId: account.profile.userId }, + await this.defaultOnDiskLocalOptions() + ) + ); + } + + protected async scaffoldNewAccountMemoryStorage(account: TAccount): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions( + { userId: account.profile.userId }, + await this.defaultOnDiskMemoryOptions() + ) + ); + if (storedAccount?.settings != null) { + storedAccount.settings.environmentUrls = account.settings.environmentUrls; + account.settings = storedAccount.settings; + } + await this.storageService.save( + account.profile.userId, + account, + await this.defaultOnDiskMemoryOptions() + ); + await this.saveAccount( + account, + this.reconcileOptions( + { userId: account.profile.userId }, + await this.defaultOnDiskMemoryOptions() + ) + ); + } + + protected async scaffoldNewAccountSessionStorage(account: TAccount): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions({ userId: account.profile.userId }, await this.defaultOnDiskOptions()) + ); + if (storedAccount?.settings != null) { + storedAccount.settings.environmentUrls = account.settings.environmentUrls; + account.settings = storedAccount.settings; + } + await this.storageService.save( + account.profile.userId, + account, + await this.defaultOnDiskMemoryOptions() + ); + await this.saveAccount( + account, + this.reconcileOptions({ userId: account.profile.userId }, await this.defaultOnDiskOptions()) + ); + } + // + + protected async pushAccounts(): Promise { + await this.pruneInMemoryAccounts(); + if (this.state?.accounts == null || Object.keys(this.state.accounts).length < 1) { + this.accounts.next(null); + return; + } + + this.accounts.next(this.state.accounts); + } + + protected reconcileOptions( + requestedOptions: StorageOptions, + defaultOptions: StorageOptions + ): StorageOptions { + if (requestedOptions == null) { + return defaultOptions; + } + requestedOptions.userId = requestedOptions?.userId ?? defaultOptions.userId; + requestedOptions.storageLocation = + requestedOptions?.storageLocation ?? defaultOptions.storageLocation; + requestedOptions.useSecureStorage = + requestedOptions?.useSecureStorage ?? defaultOptions.useSecureStorage; + requestedOptions.htmlStorageLocation = + requestedOptions?.htmlStorageLocation ?? defaultOptions.htmlStorageLocation; + requestedOptions.keySuffix = requestedOptions?.keySuffix ?? defaultOptions.keySuffix; + return requestedOptions; + } + + protected get defaultInMemoryOptions(): StorageOptions { + return { storageLocation: StorageLocation.Memory, userId: this.state.activeUserId }; + } + + protected async defaultOnDiskOptions(): Promise { + return { + storageLocation: StorageLocation.Disk, + htmlStorageLocation: HtmlStorageLocation.Session, + userId: this.state.activeUserId ?? (await this.getActiveUserIdFromStorage()), + useSecureStorage: false, + }; + } + + protected async defaultOnDiskLocalOptions(): Promise { + return { + storageLocation: StorageLocation.Disk, + htmlStorageLocation: HtmlStorageLocation.Local, + userId: this.state.activeUserId ?? (await this.getActiveUserIdFromStorage()), + useSecureStorage: false, + }; + } + + protected async defaultOnDiskMemoryOptions(): Promise { + return { + storageLocation: StorageLocation.Disk, + htmlStorageLocation: HtmlStorageLocation.Memory, + userId: this.state.activeUserId ?? (await this.getUserId()), + useSecureStorage: false, + }; + } + + protected async defaultSecureStorageOptions(): Promise { + return { + storageLocation: StorageLocation.Disk, + useSecureStorage: true, + userId: this.state.activeUserId ?? (await this.getActiveUserIdFromStorage()), + }; + } + + protected async getActiveUserIdFromStorage(): Promise { + return await this.storageService.get(keys.activeUserId); + } + + protected async removeAccountFromLocalStorage( + userId: string = this.state.activeUserId + ): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions({ userId: userId }, await this.defaultOnDiskLocalOptions()) + ); + await this.saveAccount( + this.resetAccount(storedAccount), + this.reconcileOptions({ userId: userId }, await this.defaultOnDiskLocalOptions()) + ); + } + + protected async removeAccountFromSessionStorage( + userId: string = this.state.activeUserId + ): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions({ userId: userId }, await this.defaultOnDiskOptions()) + ); + await this.saveAccount( + this.resetAccount(storedAccount), + this.reconcileOptions({ userId: userId }, await this.defaultOnDiskOptions()) + ); + } + + protected async removeAccountFromSecureStorage( + userId: string = this.state.activeUserId + ): Promise { + await this.setCryptoMasterKeyAuto(null, { userId: userId }); + await this.setCryptoMasterKeyBiometric(null, { userId: userId }); + await this.setCryptoMasterKeyB64(null, { userId: userId }); + } + + protected removeAccountFromMemory(userId: string = this.state.activeUserId): void { + delete this.state.accounts[userId]; + if (this.useAccountCache) { + this.accountDiskCache.delete(userId); + } + } + + protected async pruneInMemoryAccounts() { + // We preserve settings for logged out accounts, but we don't want to consider them when thinking about active account state + for (const userId in this.state.accounts) { + if (!(await this.getIsAuthenticated({ userId: userId }))) { + this.removeAccountFromMemory(userId); + } + } + } + + // settings persist even on reset, and are not effected by this method + protected resetAccount(account: TAccount) { + const persistentAccountInformation = { settings: account.settings }; + return Object.assign(this.createAccount(), persistentAccountInformation); + } + + protected async setAccountEnvironmentUrls(account: TAccount): Promise { + account.settings.environmentUrls = await this.getGlobalEnvironmentUrls(); + return account; + } + + protected async getGlobalEnvironmentUrls(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultOnDiskOptions()); + return (await this.getGlobals(options)).environmentUrls ?? new EnvironmentUrls(); + } + + protected clearDecryptedDataForActiveUser() { + const userId = this.state.activeUserId; + if (userId == null || this.state?.accounts[userId]?.data == null) { + return; + } + this.state.accounts[userId].data = new AccountData(); + } + + protected createAccount(init: Partial = null): TAccount { + return this.stateFactory.createAccount(init); + } + + protected createGlobals(init: Partial = null): TGlobalState { + return this.stateFactory.createGlobal(init); + } + + protected async deAuthenticateAccount(userId: string) { + await this.setAccessToken(null, { userId: userId }); + await this.setLastActive(null, { userId: userId }); + this.state.authenticatedAccounts = this.state.authenticatedAccounts.filter( + (activeUserId) => activeUserId !== userId + ); + await this.storageService.save(keys.authenticatedAccounts, this.state.authenticatedAccounts); + } + + protected async removeAccountFromDisk(userId: string) { + await this.removeAccountFromSessionStorage(userId); + await this.removeAccountFromLocalStorage(userId); + await this.removeAccountFromSecureStorage(userId); + } + + protected async dynamicallySetActiveUser() { + if (this.state.accounts == null || Object.keys(this.state.accounts).length < 1) { + await this.setActiveUser(null); + return; + } + for (const userId in this.state.accounts) { + if (userId == null) { + continue; + } + if (await this.getIsAuthenticated({ userId: userId })) { + await this.setActiveUser(userId); + break; + } + await this.setActiveUser(null); + } + } + + private async getTimeoutBasedStorageOptions(options?: StorageOptions): Promise { + const timeoutAction = await this.getVaultTimeoutAction({ userId: options?.userId }); + const timeout = await this.getVaultTimeout({ userId: options?.userId }); + const defaultOptions = + timeoutAction === "logOut" && timeout != null + ? this.defaultInMemoryOptions + : await this.defaultOnDiskOptions(); + return this.reconcileOptions(options, defaultOptions); + } + + private async saveSecureStorageKey(key: string, value: string, options?: StorageOptions) { + return value == null + ? await this.secureStorageService.remove(`${options.userId}${key}`, options) + : await this.secureStorageService.save(`${options.userId}${key}`, value, options); + } +} diff --git a/jslib/common/src/services/stateMigration.service.ts b/jslib/common/src/services/stateMigration.service.ts new file mode 100644 index 00000000..2b299898 --- /dev/null +++ b/jslib/common/src/services/stateMigration.service.ts @@ -0,0 +1,513 @@ +import { StorageService } from "../abstractions/storage.service"; +import { HtmlStorageLocation } from "../enums/htmlStorageLocation"; +import { KdfType } from "../enums/kdfType"; +import { StateVersion } from "../enums/stateVersion"; +import { ThemeType } from "../enums/themeType"; +import { StateFactory } from "../factories/stateFactory"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { EventData } from "../models/data/eventData"; +import { FolderData } from "../models/data/folderData"; +import { OrganizationData } from "../models/data/organizationData"; +import { PolicyData } from "../models/data/policyData"; +import { ProviderData } from "../models/data/providerData"; +import { SendData } from "../models/data/sendData"; +import { Account, AccountSettings } from "../models/domain/account"; +import { EnvironmentUrls } from "../models/domain/environmentUrls"; +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { GlobalState } from "../models/domain/globalState"; +import { StorageOptions } from "../models/domain/storageOptions"; + +import { TokenService } from "./token.service"; + +// Originally (before January 2022) storage was handled as a flat key/value pair store. +// With the move to a typed object for state storage these keys should no longer be in use anywhere outside of this migration. +const v1Keys: { [key: string]: string } = { + accessToken: "accessToken", + alwaysShowDock: "alwaysShowDock", + autoConfirmFingerprints: "autoConfirmFingerprints", + autoFillOnPageLoadDefault: "autoFillOnPageLoadDefault", + biometricAwaitingAcceptance: "biometricAwaitingAcceptance", + biometricFingerprintValidated: "biometricFingerprintValidated", + biometricText: "biometricText", + biometricUnlock: "biometric", + clearClipboard: "clearClipboardKey", + clientId: "apikey_clientId", + clientSecret: "apikey_clientSecret", + collapsedGroupings: "collapsedGroupings", + convertAccountToKeyConnector: "convertAccountToKeyConnector", + defaultUriMatch: "defaultUriMatch", + disableAddLoginNotification: "disableAddLoginNotification", + disableAutoBiometricsPrompt: "noAutoPromptBiometrics", + disableAutoTotpCopy: "disableAutoTotpCopy", + disableBadgeCounter: "disableBadgeCounter", + disableChangedPasswordNotification: "disableChangedPasswordNotification", + disableContextMenuItem: "disableContextMenuItem", + disableFavicon: "disableFavicon", + disableGa: "disableGa", + dontShowCardsCurrentTab: "dontShowCardsCurrentTab", + dontShowIdentitiesCurrentTab: "dontShowIdentitiesCurrentTab", + emailVerified: "emailVerified", + enableAlwaysOnTop: "enableAlwaysOnTopKey", + enableAutoFillOnPageLoad: "enableAutoFillOnPageLoad", + enableBiometric: "enabledBiometric", + enableBrowserIntegration: "enableBrowserIntegration", + enableBrowserIntegrationFingerprint: "enableBrowserIntegrationFingerprint", + enableCloseToTray: "enableCloseToTray", + enableFullWidth: "enableFullWidth", + enableGravatars: "enableGravatars", + enableMinimizeToTray: "enableMinimizeToTray", + enableStartToTray: "enableStartToTrayKey", + enableTray: "enableTray", + encKey: "encKey", // Generated Symmetric Key + encOrgKeys: "encOrgKeys", + encPrivate: "encPrivateKey", + encProviderKeys: "encProviderKeys", + entityId: "entityId", + entityType: "entityType", + environmentUrls: "environmentUrls", + equivalentDomains: "equivalentDomains", + eventCollection: "eventCollection", + forcePasswordReset: "forcePasswordReset", + history: "generatedPasswordHistory", + installedVersion: "installedVersion", + kdf: "kdf", + kdfIterations: "kdfIterations", + key: "key", // Master Key + keyHash: "keyHash", + lastActive: "lastActive", + localData: "sitesLocalData", + locale: "locale", + mainWindowSize: "mainWindowSize", + minimizeOnCopyToClipboard: "minimizeOnCopyToClipboardKey", + neverDomains: "neverDomains", + noAutoPromptBiometricsText: "noAutoPromptBiometricsText", + openAtLogin: "openAtLogin", + passwordGenerationOptions: "passwordGenerationOptions", + pinProtected: "pinProtectedKey", + protectedPin: "protectedPin", + refreshToken: "refreshToken", + ssoCodeVerifier: "ssoCodeVerifier", + ssoIdentifier: "ssoOrgIdentifier", + ssoState: "ssoState", + stamp: "securityStamp", + theme: "theme", + userEmail: "userEmail", + userId: "userId", + usesConnector: "usesKeyConnector", + vaultTimeoutAction: "vaultTimeoutAction", + vaultTimeout: "lockOption", + rememberedEmail: "rememberedEmail", +}; + +const v1KeyPrefixes: { [key: string]: string } = { + ciphers: "ciphers_", + collections: "collections_", + folders: "folders_", + lastSync: "lastSync_", + policies: "policies_", + twoFactorToken: "twoFactorToken_", + organizations: "organizations_", + providers: "providers_", + sends: "sends_", + settings: "settings_", +}; + +const keys = { + global: "global", + authenticatedAccounts: "authenticatedAccounts", + activeUserId: "activeUserId", + tempAccountSettings: "tempAccountSettings", // used to hold account specific settings (i.e clear clipboard) between initial migration and first account authentication + accountActivity: "accountActivity", +}; + +const partialKeys = { + autoKey: "_masterkey_auto", + biometricKey: "_masterkey_biometric", + masterKey: "_masterkey", +}; + +export class StateMigrationService< + TGlobalState extends GlobalState = GlobalState, + TAccount extends Account = Account +> { + constructor( + protected storageService: StorageService, + protected secureStorageService: StorageService, + protected stateFactory: StateFactory + ) {} + + async needsMigration(): Promise { + const currentStateVersion = await this.getCurrentStateVersion(); + return currentStateVersion == null || currentStateVersion < StateVersion.Latest; + } + + async migrate(): Promise { + let currentStateVersion = await this.getCurrentStateVersion(); + while (currentStateVersion < StateVersion.Latest) { + switch (currentStateVersion) { + case StateVersion.One: + await this.migrateStateFrom1To2(); + break; + case StateVersion.Two: + await this.migrateStateFrom2To3(); + break; + case StateVersion.Three: + await this.migrateStateFrom3To4(); + break; + } + + currentStateVersion += 1; + } + } + + protected async migrateStateFrom1To2(): Promise { + const clearV1Keys = async (clearingUserId?: string) => { + for (const key in v1Keys) { + if (key == null) { + continue; + } + await this.set(v1Keys[key], null); + } + if (clearingUserId != null) { + for (const keyPrefix in v1KeyPrefixes) { + if (keyPrefix == null) { + continue; + } + await this.set(v1KeyPrefixes[keyPrefix] + userId, null); + } + } + }; + + // Some processes, like biometrics, may have already defined a value before migrations are run. + // We don't want to null out those values if they don't exist in the old storage scheme (like for new installs) + // So, the OOO for migration is that we: + // 1. Check for an existing storage value from the old storage structure OR + // 2. Check for a value already set by processes that run before migration OR + // 3. Assign the default value + const globals = + (await this.get(keys.global)) ?? this.stateFactory.createGlobal(null); + globals.stateVersion = StateVersion.Two; + globals.environmentUrls = + (await this.get(v1Keys.environmentUrls)) ?? globals.environmentUrls; + globals.locale = (await this.get(v1Keys.locale)) ?? globals.locale; + globals.noAutoPromptBiometrics = + (await this.get(v1Keys.disableAutoBiometricsPrompt)) ?? + globals.noAutoPromptBiometrics; + globals.noAutoPromptBiometricsText = + (await this.get(v1Keys.noAutoPromptBiometricsText)) ?? + globals.noAutoPromptBiometricsText; + globals.ssoCodeVerifier = + (await this.get(v1Keys.ssoCodeVerifier)) ?? globals.ssoCodeVerifier; + globals.ssoOrganizationIdentifier = + (await this.get(v1Keys.ssoIdentifier)) ?? globals.ssoOrganizationIdentifier; + globals.ssoState = (await this.get(v1Keys.ssoState)) ?? globals.ssoState; + globals.rememberedEmail = + (await this.get(v1Keys.rememberedEmail)) ?? globals.rememberedEmail; + globals.theme = (await this.get(v1Keys.theme)) ?? globals.theme; + globals.vaultTimeout = (await this.get(v1Keys.vaultTimeout)) ?? globals.vaultTimeout; + globals.vaultTimeoutAction = + (await this.get(v1Keys.vaultTimeoutAction)) ?? globals.vaultTimeoutAction; + globals.window = (await this.get(v1Keys.mainWindowSize)) ?? globals.window; + globals.enableTray = (await this.get(v1Keys.enableTray)) ?? globals.enableTray; + globals.enableMinimizeToTray = + (await this.get(v1Keys.enableMinimizeToTray)) ?? globals.enableMinimizeToTray; + globals.enableCloseToTray = + (await this.get(v1Keys.enableCloseToTray)) ?? globals.enableCloseToTray; + globals.enableStartToTray = + (await this.get(v1Keys.enableStartToTray)) ?? globals.enableStartToTray; + globals.openAtLogin = (await this.get(v1Keys.openAtLogin)) ?? globals.openAtLogin; + globals.alwaysShowDock = + (await this.get(v1Keys.alwaysShowDock)) ?? globals.alwaysShowDock; + globals.enableBrowserIntegration = + (await this.get(v1Keys.enableBrowserIntegration)) ?? + globals.enableBrowserIntegration; + globals.enableBrowserIntegrationFingerprint = + (await this.get(v1Keys.enableBrowserIntegrationFingerprint)) ?? + globals.enableBrowserIntegrationFingerprint; + + const userId = + (await this.get(v1Keys.userId)) ?? (await this.get(v1Keys.entityId)); + + const defaultAccount = this.stateFactory.createAccount(null); + const accountSettings: AccountSettings = { + autoConfirmFingerPrints: + (await this.get(v1Keys.autoConfirmFingerprints)) ?? + defaultAccount.settings.autoConfirmFingerPrints, + autoFillOnPageLoadDefault: + (await this.get(v1Keys.autoFillOnPageLoadDefault)) ?? + defaultAccount.settings.autoFillOnPageLoadDefault, + biometricLocked: null, + biometricUnlock: + (await this.get(v1Keys.biometricUnlock)) ?? + defaultAccount.settings.biometricUnlock, + clearClipboard: + (await this.get(v1Keys.clearClipboard)) ?? defaultAccount.settings.clearClipboard, + defaultUriMatch: + (await this.get(v1Keys.defaultUriMatch)) ?? defaultAccount.settings.defaultUriMatch, + disableAddLoginNotification: + (await this.get(v1Keys.disableAddLoginNotification)) ?? + defaultAccount.settings.disableAddLoginNotification, + disableAutoBiometricsPrompt: + (await this.get(v1Keys.disableAutoBiometricsPrompt)) ?? + defaultAccount.settings.disableAutoBiometricsPrompt, + disableAutoTotpCopy: + (await this.get(v1Keys.disableAutoTotpCopy)) ?? + defaultAccount.settings.disableAutoTotpCopy, + disableBadgeCounter: + (await this.get(v1Keys.disableBadgeCounter)) ?? + defaultAccount.settings.disableBadgeCounter, + disableChangedPasswordNotification: + (await this.get(v1Keys.disableChangedPasswordNotification)) ?? + defaultAccount.settings.disableChangedPasswordNotification, + disableContextMenuItem: + (await this.get(v1Keys.disableContextMenuItem)) ?? + defaultAccount.settings.disableContextMenuItem, + disableGa: (await this.get(v1Keys.disableGa)) ?? defaultAccount.settings.disableGa, + dontShowCardsCurrentTab: + (await this.get(v1Keys.dontShowCardsCurrentTab)) ?? + defaultAccount.settings.dontShowCardsCurrentTab, + dontShowIdentitiesCurrentTab: + (await this.get(v1Keys.dontShowIdentitiesCurrentTab)) ?? + defaultAccount.settings.dontShowIdentitiesCurrentTab, + enableAlwaysOnTop: + (await this.get(v1Keys.enableAlwaysOnTop)) ?? + defaultAccount.settings.enableAlwaysOnTop, + enableAutoFillOnPageLoad: + (await this.get(v1Keys.enableAutoFillOnPageLoad)) ?? + defaultAccount.settings.enableAutoFillOnPageLoad, + enableBiometric: + (await this.get(v1Keys.enableBiometric)) ?? + defaultAccount.settings.enableBiometric, + enableFullWidth: + (await this.get(v1Keys.enableFullWidth)) ?? + defaultAccount.settings.enableFullWidth, + enableGravitars: + (await this.get(v1Keys.enableGravatars)) ?? + defaultAccount.settings.enableGravitars, + environmentUrls: globals.environmentUrls ?? defaultAccount.settings.environmentUrls, + equivalentDomains: + (await this.get(v1Keys.equivalentDomains)) ?? + defaultAccount.settings.equivalentDomains, + minimizeOnCopyToClipboard: + (await this.get(v1Keys.minimizeOnCopyToClipboard)) ?? + defaultAccount.settings.minimizeOnCopyToClipboard, + neverDomains: + (await this.get(v1Keys.neverDomains)) ?? defaultAccount.settings.neverDomains, + passwordGenerationOptions: + (await this.get(v1Keys.passwordGenerationOptions)) ?? + defaultAccount.settings.passwordGenerationOptions, + pinProtected: { + decrypted: null, + encrypted: await this.get(v1Keys.pinProtected), + }, + protectedPin: await this.get(v1Keys.protectedPin), + settings: userId == null ? null : await this.get(v1KeyPrefixes.settings + userId), + vaultTimeout: + (await this.get(v1Keys.vaultTimeout)) ?? defaultAccount.settings.vaultTimeout, + vaultTimeoutAction: + (await this.get(v1Keys.vaultTimeoutAction)) ?? + defaultAccount.settings.vaultTimeoutAction, + }; + + // (userId == null) = no logged in user (so no known userId) and we need to temporarily store account specific settings in state to migrate on first auth + // (userId != null) = we have a currently authed user (so known userId) with encrypted data and other key settings we can move, no need to temporarily store account settings + if (userId == null) { + await this.set(keys.tempAccountSettings, accountSettings); + await this.set(keys.global, globals); + await this.set(keys.authenticatedAccounts, []); + await this.set(keys.activeUserId, null); + await clearV1Keys(); + return; + } + + globals.twoFactorToken = await this.get(v1KeyPrefixes.twoFactorToken + userId); + await this.set(keys.global, globals); + await this.set(userId, { + data: { + addEditCipherInfo: null, + ciphers: { + decrypted: null, + encrypted: await this.get<{ [id: string]: CipherData }>(v1KeyPrefixes.ciphers + userId), + }, + collapsedGroupings: null, + collections: { + decrypted: null, + encrypted: await this.get<{ [id: string]: CollectionData }>( + v1KeyPrefixes.collections + userId + ), + }, + eventCollection: await this.get(v1Keys.eventCollection), + folders: { + decrypted: null, + encrypted: await this.get<{ [id: string]: FolderData }>(v1KeyPrefixes.folders + userId), + }, + localData: null, + organizations: await this.get<{ [id: string]: OrganizationData }>( + v1KeyPrefixes.organizations + userId + ), + passwordGenerationHistory: { + decrypted: null, + encrypted: await this.get(v1Keys.history), + }, + policies: { + decrypted: null, + encrypted: await this.get<{ [id: string]: PolicyData }>(v1KeyPrefixes.policies + userId), + }, + providers: await this.get<{ [id: string]: ProviderData }>(v1KeyPrefixes.providers + userId), + sends: { + decrypted: null, + encrypted: await this.get<{ [id: string]: SendData }>(v1KeyPrefixes.sends + userId), + }, + }, + keys: { + apiKeyClientSecret: await this.get(v1Keys.clientSecret), + cryptoMasterKey: null, + cryptoMasterKeyAuto: null, + cryptoMasterKeyB64: null, + cryptoMasterKeyBiometric: null, + cryptoSymmetricKey: { + encrypted: await this.get(v1Keys.encKey), + decrypted: null, + }, + legacyEtmKey: null, + organizationKeys: { + decrypted: null, + encrypted: await this.get(v1Keys.encOrgKeys), + }, + privateKey: { + decrypted: null, + encrypted: await this.get(v1Keys.encPrivate), + }, + providerKeys: { + decrypted: null, + encrypted: await this.get(v1Keys.encProviderKeys), + }, + publicKey: null, + }, + profile: { + apiKeyClientId: await this.get(v1Keys.clientId), + authenticationStatus: null, + convertAccountToKeyConnector: await this.get(v1Keys.convertAccountToKeyConnector), + email: await this.get(v1Keys.userEmail), + emailVerified: await this.get(v1Keys.emailVerified), + entityId: null, + entityType: null, + everBeenUnlocked: null, + forcePasswordReset: null, + hasPremiumPersonally: null, + kdfIterations: await this.get(v1Keys.kdfIterations), + kdfType: await this.get(v1Keys.kdf), + keyHash: await this.get(v1Keys.keyHash), + lastSync: null, + userId: userId, + usesKeyConnector: null, + }, + settings: accountSettings, + tokens: { + accessToken: await this.get(v1Keys.accessToken), + decodedToken: null, + refreshToken: await this.get(v1Keys.refreshToken), + securityStamp: null, + }, + }); + + await this.set(keys.authenticatedAccounts, [userId]); + await this.set(keys.activeUserId, userId); + + const accountActivity: { [userId: string]: number } = { + [userId]: await this.get(v1Keys.lastActive), + }; + accountActivity[userId] = await this.get(v1Keys.lastActive); + await this.set(keys.accountActivity, accountActivity); + + await clearV1Keys(userId); + + if (await this.secureStorageService.has(v1Keys.key, { keySuffix: "biometric" })) { + await this.secureStorageService.save( + `${userId}${partialKeys.biometricKey}`, + await this.secureStorageService.get(v1Keys.key, { keySuffix: "biometric" }), + { keySuffix: "biometric" } + ); + await this.secureStorageService.remove(v1Keys.key, { keySuffix: "biometric" }); + } + + if (await this.secureStorageService.has(v1Keys.key, { keySuffix: "auto" })) { + await this.secureStorageService.save( + `${userId}${partialKeys.autoKey}`, + await this.secureStorageService.get(v1Keys.key, { keySuffix: "auto" }), + { keySuffix: "auto" } + ); + await this.secureStorageService.remove(v1Keys.key, { keySuffix: "auto" }); + } + + if (await this.secureStorageService.has(v1Keys.key)) { + await this.secureStorageService.save( + `${userId}${partialKeys.masterKey}`, + await this.secureStorageService.get(v1Keys.key) + ); + await this.secureStorageService.remove(v1Keys.key); + } + } + + protected async migrateStateFrom2To3(): Promise { + const authenticatedUserIds = await this.get(keys.authenticatedAccounts); + await Promise.all( + authenticatedUserIds.map(async (userId) => { + const account = await this.get(userId); + if ( + account?.profile?.hasPremiumPersonally === null && + account.tokens?.accessToken != null + ) { + const decodedToken = await TokenService.decodeToken(account.tokens.accessToken); + account.profile.hasPremiumPersonally = decodedToken.premium; + await this.set(userId, account); + } + }) + ); + + const globals = await this.getGlobals(); + globals.stateVersion = StateVersion.Three; + await this.set(keys.global, globals); + } + + protected async migrateStateFrom3To4(): Promise { + const authenticatedUserIds = await this.get(keys.authenticatedAccounts); + await Promise.all( + authenticatedUserIds.map(async (userId) => { + const account = await this.get(userId); + if (account?.profile?.everBeenUnlocked != null) { + delete account.profile.everBeenUnlocked; + return this.set(userId, account); + } + }) + ); + + const globals = await this.getGlobals(); + globals.stateVersion = StateVersion.Four; + await this.set(keys.global, globals); + } + + protected get options(): StorageOptions { + return { htmlStorageLocation: HtmlStorageLocation.Local }; + } + + protected get(key: string): Promise { + return this.storageService.get(key, this.options); + } + + protected set(key: string, value: any): Promise { + if (value == null) { + return this.storageService.remove(key, this.options); + } + return this.storageService.save(key, value, this.options); + } + + protected async getGlobals(): Promise { + return await this.get(keys.global); + } + + protected async getCurrentStateVersion(): Promise { + return (await this.getGlobals())?.stateVersion ?? StateVersion.One; + } +} diff --git a/jslib/common/src/services/sync.service.ts b/jslib/common/src/services/sync.service.ts new file mode 100644 index 00000000..93611238 --- /dev/null +++ b/jslib/common/src/services/sync.service.ts @@ -0,0 +1,400 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { CollectionService } from "../abstractions/collection.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FolderService } from "../abstractions/folder.service"; +import { KeyConnectorService } from "../abstractions/keyConnector.service"; +import { LogService } from "../abstractions/log.service"; +import { MessagingService } from "../abstractions/messaging.service"; +import { OrganizationService } from "../abstractions/organization.service"; +import { PolicyService } from "../abstractions/policy.service"; +import { ProviderService } from "../abstractions/provider.service"; +import { SendService } from "../abstractions/send.service"; +import { SettingsService } from "../abstractions/settings.service"; +import { StateService } from "../abstractions/state.service"; +import { SyncService as SyncServiceAbstraction } from "../abstractions/sync.service"; +import { sequentialize } from "../misc/sequentialize"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { FolderData } from "../models/data/folderData"; +import { OrganizationData } from "../models/data/organizationData"; +import { PolicyData } from "../models/data/policyData"; +import { ProviderData } from "../models/data/providerData"; +import { SendData } from "../models/data/sendData"; +import { CipherResponse } from "../models/response/cipherResponse"; +import { CollectionDetailsResponse } from "../models/response/collectionResponse"; +import { DomainsResponse } from "../models/response/domainsResponse"; +import { FolderResponse } from "../models/response/folderResponse"; +import { + SyncCipherNotification, + SyncFolderNotification, + SyncSendNotification, +} from "../models/response/notificationResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; +import { ProfileResponse } from "../models/response/profileResponse"; +import { SendResponse } from "../models/response/sendResponse"; + +export class SyncService implements SyncServiceAbstraction { + syncInProgress = false; + + constructor( + private apiService: ApiService, + private settingsService: SettingsService, + private folderService: FolderService, + private cipherService: CipherService, + private cryptoService: CryptoService, + private collectionService: CollectionService, + private messagingService: MessagingService, + private policyService: PolicyService, + private sendService: SendService, + private logService: LogService, + private keyConnectorService: KeyConnectorService, + private stateService: StateService, + private organizationService: OrganizationService, + private providerService: ProviderService, + private logoutCallback: (expired: boolean) => Promise + ) {} + + async getLastSync(): Promise { + if ((await this.stateService.getUserId()) == null) { + return null; + } + + const lastSync = await this.stateService.getLastSync(); + if (lastSync) { + return new Date(lastSync); + } + + return null; + } + + async setLastSync(date: Date, userId?: string): Promise { + await this.stateService.setLastSync(date.toJSON(), { userId: userId }); + } + + @sequentialize(() => "fullSync") + async fullSync(forceSync: boolean, allowThrowOnError = false): Promise { + this.syncStarted(); + const isAuthenticated = await this.stateService.getIsAuthenticated(); + if (!isAuthenticated) { + return this.syncCompleted(false); + } + + const now = new Date(); + let needsSync = false; + try { + needsSync = await this.needsSyncing(forceSync); + } catch (e) { + if (allowThrowOnError) { + throw e; + } + } + + if (!needsSync) { + await this.setLastSync(now); + return this.syncCompleted(false); + } + + const userId = await this.stateService.getUserId(); + try { + await this.apiService.refreshIdentityToken(); + const response = await this.apiService.getSync(); + + await this.syncProfile(response.profile); + await this.syncFolders(userId, response.folders); + await this.syncCollections(response.collections); + await this.syncCiphers(userId, response.ciphers); + await this.syncSends(userId, response.sends); + await this.syncSettings(response.domains); + await this.syncPolicies(response.policies); + + await this.setLastSync(now); + return this.syncCompleted(true); + } catch (e) { + if (allowThrowOnError) { + throw e; + } else { + return this.syncCompleted(false); + } + } + } + + async syncUpsertFolder(notification: SyncFolderNotification, isEdit: boolean): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + try { + const localFolder = await this.folderService.get(notification.id); + if ( + (!isEdit && localFolder == null) || + (isEdit && localFolder != null && localFolder.revisionDate < notification.revisionDate) + ) { + const remoteFolder = await this.apiService.getFolder(notification.id); + if (remoteFolder != null) { + const userId = await this.stateService.getUserId(); + await this.folderService.upsert(new FolderData(remoteFolder, userId)); + this.messagingService.send("syncedUpsertedFolder", { folderId: notification.id }); + return this.syncCompleted(true); + } + } + } catch (e) { + this.logService.error(e); + } + } + return this.syncCompleted(false); + } + + async syncDeleteFolder(notification: SyncFolderNotification): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + await this.folderService.delete(notification.id); + this.messagingService.send("syncedDeletedFolder", { folderId: notification.id }); + this.syncCompleted(true); + return true; + } + return this.syncCompleted(false); + } + + async syncUpsertCipher(notification: SyncCipherNotification, isEdit: boolean): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + try { + let shouldUpdate = true; + const localCipher = await this.cipherService.get(notification.id); + if (localCipher != null && localCipher.revisionDate >= notification.revisionDate) { + shouldUpdate = false; + } + + let checkCollections = false; + if (shouldUpdate) { + if (isEdit) { + shouldUpdate = localCipher != null; + checkCollections = true; + } else { + if (notification.collectionIds == null || notification.organizationId == null) { + shouldUpdate = localCipher == null; + } else { + shouldUpdate = false; + checkCollections = true; + } + } + } + + if ( + !shouldUpdate && + checkCollections && + notification.organizationId != null && + notification.collectionIds != null && + notification.collectionIds.length > 0 + ) { + const collections = await this.collectionService.getAll(); + if (collections != null) { + for (let i = 0; i < collections.length; i++) { + if (notification.collectionIds.indexOf(collections[i].id) > -1) { + shouldUpdate = true; + break; + } + } + } + } + + if (shouldUpdate) { + const remoteCipher = await this.apiService.getCipher(notification.id); + if (remoteCipher != null) { + const userId = await this.stateService.getUserId(); + await this.cipherService.upsert(new CipherData(remoteCipher, userId)); + this.messagingService.send("syncedUpsertedCipher", { cipherId: notification.id }); + return this.syncCompleted(true); + } + } + } catch (e) { + if (e != null && e.statusCode === 404 && isEdit) { + await this.cipherService.delete(notification.id); + this.messagingService.send("syncedDeletedCipher", { cipherId: notification.id }); + return this.syncCompleted(true); + } + } + } + return this.syncCompleted(false); + } + + async syncDeleteCipher(notification: SyncCipherNotification): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + await this.cipherService.delete(notification.id); + this.messagingService.send("syncedDeletedCipher", { cipherId: notification.id }); + return this.syncCompleted(true); + } + return this.syncCompleted(false); + } + + async syncUpsertSend(notification: SyncSendNotification, isEdit: boolean): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + try { + const localSend = await this.sendService.get(notification.id); + if ( + (!isEdit && localSend == null) || + (isEdit && localSend != null && localSend.revisionDate < notification.revisionDate) + ) { + const remoteSend = await this.apiService.getSend(notification.id); + if (remoteSend != null) { + const userId = await this.stateService.getUserId(); + await this.sendService.upsert(new SendData(remoteSend, userId)); + this.messagingService.send("syncedUpsertedSend", { sendId: notification.id }); + return this.syncCompleted(true); + } + } + } catch (e) { + this.logService.error(e); + } + } + return this.syncCompleted(false); + } + + async syncDeleteSend(notification: SyncSendNotification): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + await this.sendService.delete(notification.id); + this.messagingService.send("syncedDeletedSend", { sendId: notification.id }); + this.syncCompleted(true); + return true; + } + return this.syncCompleted(false); + } + + // Helpers + + private syncStarted() { + this.syncInProgress = true; + this.messagingService.send("syncStarted"); + } + + private syncCompleted(successfully: boolean): boolean { + this.syncInProgress = false; + this.messagingService.send("syncCompleted", { successfully: successfully }); + return successfully; + } + + private async needsSyncing(forceSync: boolean) { + if (forceSync) { + return true; + } + + const lastSync = await this.getLastSync(); + if (lastSync == null || lastSync.getTime() === 0) { + return true; + } + + const response = await this.apiService.getAccountRevisionDate(); + if (new Date(response) <= lastSync) { + return false; + } + return true; + } + + private async syncProfile(response: ProfileResponse) { + const stamp = await this.stateService.getSecurityStamp(); + if (stamp != null && stamp !== response.securityStamp) { + if (this.logoutCallback != null) { + await this.logoutCallback(true); + } + + throw new Error("Stamp has changed"); + } + + await this.cryptoService.setEncKey(response.key); + await this.cryptoService.setEncPrivateKey(response.privateKey); + await this.cryptoService.setProviderKeys(response.providers); + await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations); + await this.stateService.setSecurityStamp(response.securityStamp); + await this.stateService.setEmailVerified(response.emailVerified); + await this.stateService.setForcePasswordReset(response.forcePasswordReset); + await this.keyConnectorService.setUsesKeyConnector(response.usesKeyConnector); + + const organizations: { [id: string]: OrganizationData } = {}; + response.organizations.forEach((o) => { + organizations[o.id] = new OrganizationData(o); + }); + + const providers: { [id: string]: ProviderData } = {}; + response.providers.forEach((p) => { + providers[p.id] = new ProviderData(p); + }); + + response.providerOrganizations.forEach((o) => { + if (organizations[o.id] == null) { + organizations[o.id] = new OrganizationData(o); + organizations[o.id].isProviderUser = true; + } + }); + + await this.organizationService.save(organizations); + await this.providerService.save(providers); + + if (await this.keyConnectorService.userNeedsMigration()) { + await this.keyConnectorService.setConvertAccountRequired(true); + this.messagingService.send("convertAccountToKeyConnector"); + } else { + this.keyConnectorService.removeConvertAccountRequired(); + } + } + + private async syncFolders(userId: string, response: FolderResponse[]) { + const folders: { [id: string]: FolderData } = {}; + response.forEach((f) => { + folders[f.id] = new FolderData(f, userId); + }); + return await this.folderService.replace(folders); + } + + private async syncCollections(response: CollectionDetailsResponse[]) { + const collections: { [id: string]: CollectionData } = {}; + response.forEach((c) => { + collections[c.id] = new CollectionData(c); + }); + return await this.collectionService.replace(collections); + } + + private async syncCiphers(userId: string, response: CipherResponse[]) { + const ciphers: { [id: string]: CipherData } = {}; + response.forEach((c) => { + ciphers[c.id] = new CipherData(c, userId); + }); + return await this.cipherService.replace(ciphers); + } + + private async syncSends(userId: string, response: SendResponse[]) { + const sends: { [id: string]: SendData } = {}; + response.forEach((s) => { + sends[s.id] = new SendData(s, userId); + }); + return await this.sendService.replace(sends); + } + + private async syncSettings(response: DomainsResponse) { + let eqDomains: string[][] = []; + if (response != null && response.equivalentDomains != null) { + eqDomains = eqDomains.concat(response.equivalentDomains); + } + + if (response != null && response.globalEquivalentDomains != null) { + response.globalEquivalentDomains.forEach((global) => { + if (global.domains.length > 0) { + eqDomains.push(global.domains); + } + }); + } + + return this.settingsService.setEquivalentDomains(eqDomains); + } + + private async syncPolicies(response: PolicyResponse[]) { + const policies: { [id: string]: PolicyData } = {}; + if (response != null) { + response.forEach((p) => { + policies[p.id] = new PolicyData(p); + }); + } + return await this.policyService.replace(policies); + } +} diff --git a/jslib/common/src/services/system.service.ts b/jslib/common/src/services/system.service.ts new file mode 100644 index 00000000..949b8ca4 --- /dev/null +++ b/jslib/common/src/services/system.service.ts @@ -0,0 +1,90 @@ +import { MessagingService } from "../abstractions/messaging.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { StateService } from "../abstractions/state.service"; +import { SystemService as SystemServiceAbstraction } from "../abstractions/system.service"; +import { Utils } from "../misc/utils"; + +export class SystemService implements SystemServiceAbstraction { + private reloadInterval: any = null; + private clearClipboardTimeout: any = null; + private clearClipboardTimeoutFunction: () => Promise = null; + + constructor( + private messagingService: MessagingService, + private platformUtilsService: PlatformUtilsService, + private reloadCallback: () => Promise = null, + private stateService: StateService + ) {} + + async startProcessReload(): Promise { + if ( + (await this.stateService.getDecryptedPinProtected()) != null || + (await this.stateService.getBiometricLocked()) || + this.reloadInterval != null + ) { + return; + } + this.cancelProcessReload(); + this.reloadInterval = setInterval(async () => { + let doRefresh = false; + const lastActive = await this.stateService.getLastActive(); + if (lastActive != null) { + const diffSeconds = new Date().getTime() - lastActive; + // Don't refresh if they are still active in the window + doRefresh = diffSeconds >= 5000; + } + const biometricLockedFingerprintValidated = + (await this.stateService.getBiometricFingerprintValidated()) && + (await this.stateService.getBiometricLocked()); + if (doRefresh && !biometricLockedFingerprintValidated) { + clearInterval(this.reloadInterval); + this.reloadInterval = null; + this.messagingService.send("reloadProcess"); + if (this.reloadCallback != null) { + await this.reloadCallback(); + } + } + }, 10000); + } + + cancelProcessReload(): void { + if (this.reloadInterval != null) { + clearInterval(this.reloadInterval); + this.reloadInterval = null; + } + } + + async clearClipboard(clipboardValue: string, timeoutMs: number = null): Promise { + if (this.clearClipboardTimeout != null) { + clearTimeout(this.clearClipboardTimeout); + this.clearClipboardTimeout = null; + } + if (Utils.isNullOrWhitespace(clipboardValue)) { + return; + } + await this.stateService.getClearClipboard().then((clearSeconds) => { + if (clearSeconds == null) { + return; + } + if (timeoutMs == null) { + timeoutMs = clearSeconds * 1000; + } + this.clearClipboardTimeoutFunction = async () => { + const clipboardValueNow = await this.platformUtilsService.readFromClipboard(); + if (clipboardValue === clipboardValueNow) { + this.platformUtilsService.copyToClipboard("", { clearing: true }); + } + }; + this.clearClipboardTimeout = setTimeout(async () => { + await this.clearPendingClipboard(); + }, timeoutMs); + }); + } + + async clearPendingClipboard() { + if (this.clearClipboardTimeoutFunction != null) { + await this.clearClipboardTimeoutFunction(); + this.clearClipboardTimeoutFunction = null; + } + } +} diff --git a/jslib/common/src/services/token.service.ts b/jslib/common/src/services/token.service.ts new file mode 100644 index 00000000..a47cca7c --- /dev/null +++ b/jslib/common/src/services/token.service.ts @@ -0,0 +1,195 @@ +import { StateService } from "../abstractions/state.service"; +import { TokenService as TokenServiceAbstraction } from "../abstractions/token.service"; +import { Utils } from "../misc/utils"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; + +export class TokenService implements TokenServiceAbstraction { + static decodeToken(token: string): Promise { + if (token == null) { + throw new Error("Token not provided."); + } + + const parts = token.split("."); + if (parts.length !== 3) { + throw new Error("JWT must have 3 parts"); + } + + const decoded = Utils.fromUrlB64ToUtf8(parts[1]); + if (decoded == null) { + throw new Error("Cannot decode the token"); + } + + const decodedToken = JSON.parse(decoded); + return decodedToken; + } + + constructor(private stateService: StateService) {} + + async setTokens( + accessToken: string, + refreshToken: string, + clientIdClientSecret: [string, string] + ): Promise { + await this.setToken(accessToken); + await this.setRefreshToken(refreshToken); + if (clientIdClientSecret != null) { + await this.setClientId(clientIdClientSecret[0]); + await this.setClientSecret(clientIdClientSecret[1]); + } + } + + async setClientId(clientId: string): Promise { + return await this.stateService.setApiKeyClientId(clientId); + } + + async getClientId(): Promise { + return await this.stateService.getApiKeyClientId(); + } + + async setClientSecret(clientSecret: string): Promise { + return await this.stateService.setApiKeyClientSecret(clientSecret); + } + + async getClientSecret(): Promise { + return await this.stateService.getApiKeyClientSecret(); + } + + async setToken(token: string): Promise { + await this.stateService.setAccessToken(token); + } + + async getToken(): Promise { + return await this.stateService.getAccessToken(); + } + + async setRefreshToken(refreshToken: string): Promise { + return await this.stateService.setRefreshToken(refreshToken); + } + + async getRefreshToken(): Promise { + return await this.stateService.getRefreshToken(); + } + + async setTwoFactorToken(tokenResponse: IdentityTokenResponse): Promise { + return await this.stateService.setTwoFactorToken(tokenResponse.twoFactorToken); + } + + async getTwoFactorToken(): Promise { + return await this.stateService.getTwoFactorToken(); + } + + async clearTwoFactorToken(): Promise { + return await this.stateService.setTwoFactorToken(null); + } + + async clearToken(userId?: string): Promise { + await this.stateService.setAccessToken(null, { userId: userId }); + await this.stateService.setRefreshToken(null, { userId: userId }); + await this.stateService.setApiKeyClientId(null, { userId: userId }); + await this.stateService.setApiKeyClientSecret(null, { userId: userId }); + } + + // jwthelper methods + // ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js + + async decodeToken(token?: string): Promise { + const storedToken = await this.stateService.getDecodedToken(); + if (token === null && storedToken != null) { + return storedToken; + } + + token = token ?? (await this.stateService.getAccessToken()); + + if (token == null) { + throw new Error("Token not found."); + } + + return TokenService.decodeToken(token); + } + + async getTokenExpirationDate(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.exp === "undefined") { + return null; + } + + const d = new Date(0); // The 0 here is the key, which sets the date to the epoch + d.setUTCSeconds(decoded.exp); + return d; + } + + async tokenSecondsRemaining(offsetSeconds = 0): Promise { + const d = await this.getTokenExpirationDate(); + if (d == null) { + return 0; + } + + const msRemaining = d.valueOf() - (new Date().valueOf() + offsetSeconds * 1000); + return Math.round(msRemaining / 1000); + } + + async tokenNeedsRefresh(minutes = 5): Promise { + const sRemaining = await this.tokenSecondsRemaining(); + return sRemaining < 60 * minutes; + } + + async getUserId(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.sub === "undefined") { + throw new Error("No user id found"); + } + + return decoded.sub as string; + } + + async getEmail(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.email === "undefined") { + throw new Error("No email found"); + } + + return decoded.email as string; + } + + async getEmailVerified(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.email_verified === "undefined") { + throw new Error("No email verification found"); + } + + return decoded.email_verified as boolean; + } + + async getName(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.name === "undefined") { + return null; + } + + return decoded.name as string; + } + + async getPremium(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.premium === "undefined") { + return false; + } + + return decoded.premium as boolean; + } + + async getIssuer(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.iss === "undefined") { + throw new Error("No issuer found"); + } + + return decoded.iss as string; + } + + async getIsExternal(): Promise { + const decoded = await this.decodeToken(); + + return Array.isArray(decoded.amr) && decoded.amr.includes("external"); + } +} diff --git a/jslib/common/src/services/totp.service.ts b/jslib/common/src/services/totp.service.ts new file mode 100644 index 00000000..730587c7 --- /dev/null +++ b/jslib/common/src/services/totp.service.ts @@ -0,0 +1,174 @@ +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { LogService } from "../abstractions/log.service"; +import { StateService } from "../abstractions/state.service"; +import { TotpService as TotpServiceAbstraction } from "../abstractions/totp.service"; +import { Utils } from "../misc/utils"; + +const B32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; +const SteamChars = "23456789BCDFGHJKMNPQRTVWXY"; + +export class TotpService implements TotpServiceAbstraction { + constructor( + private cryptoFunctionService: CryptoFunctionService, + private logService: LogService, + private stateService: StateService + ) {} + + async getCode(key: string): Promise { + if (key == null) { + return null; + } + let period = 30; + let alg: "sha1" | "sha256" | "sha512" = "sha1"; + let digits = 6; + let keyB32 = key; + const isOtpAuth = key.toLowerCase().indexOf("otpauth://") === 0; + const isSteamAuth = !isOtpAuth && key.toLowerCase().indexOf("steam://") === 0; + if (isOtpAuth) { + const params = Utils.getQueryParams(key); + if (params.has("digits") && params.get("digits") != null) { + try { + const digitParams = parseInt(params.get("digits").trim(), null); + if (digitParams > 10) { + digits = 10; + } else if (digitParams > 0) { + digits = digitParams; + } + } catch { + this.logService.error("Invalid digits param."); + } + } + if (params.has("period") && params.get("period") != null) { + try { + const periodParam = parseInt(params.get("period").trim(), null); + if (periodParam > 0) { + period = periodParam; + } + } catch { + this.logService.error("Invalid period param."); + } + } + if (params.has("secret") && params.get("secret") != null) { + keyB32 = params.get("secret"); + } + if (params.has("algorithm") && params.get("algorithm") != null) { + const algParam = params.get("algorithm").toLowerCase(); + if (algParam === "sha1" || algParam === "sha256" || algParam === "sha512") { + alg = algParam; + } + } + } else if (isSteamAuth) { + keyB32 = key.substr("steam://".length); + digits = 5; + } + + const epoch = Math.round(new Date().getTime() / 1000.0); + const timeHex = this.leftPad(this.decToHex(Math.floor(epoch / period)), 16, "0"); + const timeBytes = Utils.fromHexToArray(timeHex); + const keyBytes = this.b32ToBytes(keyB32); + + if (!keyBytes.length || !timeBytes.length) { + return null; + } + + const hash = await this.sign(keyBytes, timeBytes, alg); + if (hash.length === 0) { + return null; + } + + const offset = hash[hash.length - 1] & 0xf; + const binary = + ((hash[offset] & 0x7f) << 24) | + ((hash[offset + 1] & 0xff) << 16) | + ((hash[offset + 2] & 0xff) << 8) | + (hash[offset + 3] & 0xff); + + let otp = ""; + if (isSteamAuth) { + let fullCode = binary & 0x7fffffff; + for (let i = 0; i < digits; i++) { + otp += SteamChars[fullCode % SteamChars.length]; + fullCode = Math.trunc(fullCode / SteamChars.length); + } + } else { + otp = (binary % Math.pow(10, digits)).toString(); + otp = this.leftPad(otp, digits, "0"); + } + + return otp; + } + + getTimeInterval(key: string): number { + let period = 30; + if (key != null && key.toLowerCase().indexOf("otpauth://") === 0) { + const params = Utils.getQueryParams(key); + if (params.has("period") && params.get("period") != null) { + try { + period = parseInt(params.get("period").trim(), null); + } catch { + this.logService.error("Invalid period param."); + } + } + } + return period; + } + + async isAutoCopyEnabled(): Promise { + return !(await this.stateService.getDisableAutoTotpCopy()); + } + + // Helpers + + private leftPad(s: string, l: number, p: string): string { + if (l + 1 >= s.length) { + s = Array(l + 1 - s.length).join(p) + s; + } + return s; + } + + private decToHex(d: number): string { + return (d < 15.5 ? "0" : "") + Math.round(d).toString(16); + } + + private b32ToHex(s: string): string { + s = s.toUpperCase(); + let cleanedInput = ""; + + for (let i = 0; i < s.length; i++) { + if (B32Chars.indexOf(s[i]) < 0) { + continue; + } + + cleanedInput += s[i]; + } + s = cleanedInput; + + let bits = ""; + let hex = ""; + for (let i = 0; i < s.length; i++) { + const byteIndex = B32Chars.indexOf(s.charAt(i)); + if (byteIndex < 0) { + continue; + } + bits += this.leftPad(byteIndex.toString(2), 5, "0"); + } + for (let i = 0; i + 4 <= bits.length; i += 4) { + const chunk = bits.substr(i, 4); + hex = hex + parseInt(chunk, 2).toString(16); + } + return hex; + } + + private b32ToBytes(s: string): Uint8Array { + return Utils.fromHexToArray(this.b32ToHex(s)); + } + + private async sign( + keyBytes: Uint8Array, + timeBytes: Uint8Array, + alg: "sha1" | "sha256" | "sha512" + ) { + const signature = await this.cryptoFunctionService.hmac(timeBytes.buffer, keyBytes.buffer, alg); + return new Uint8Array(signature); + } +} diff --git a/jslib/common/src/services/twoFactor.service.ts b/jslib/common/src/services/twoFactor.service.ts new file mode 100644 index 00000000..7720d9be --- /dev/null +++ b/jslib/common/src/services/twoFactor.service.ts @@ -0,0 +1,186 @@ +import { I18nService } from "../abstractions/i18n.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { + TwoFactorProviderDetails, + TwoFactorService as TwoFactorServiceAbstraction, +} from "../abstractions/twoFactor.service"; +import { TwoFactorProviderType } from "../enums/twoFactorProviderType"; +import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; + +export const TwoFactorProviders: Partial> = + { + [TwoFactorProviderType.Authenticator]: { + type: TwoFactorProviderType.Authenticator, + name: null as string, + description: null as string, + priority: 1, + sort: 1, + premium: false, + }, + [TwoFactorProviderType.Yubikey]: { + type: TwoFactorProviderType.Yubikey, + name: null as string, + description: null as string, + priority: 3, + sort: 2, + premium: true, + }, + [TwoFactorProviderType.Duo]: { + type: TwoFactorProviderType.Duo, + name: "Duo", + description: null as string, + priority: 2, + sort: 3, + premium: true, + }, + [TwoFactorProviderType.OrganizationDuo]: { + type: TwoFactorProviderType.OrganizationDuo, + name: "Duo (Organization)", + description: null as string, + priority: 10, + sort: 4, + premium: false, + }, + [TwoFactorProviderType.Email]: { + type: TwoFactorProviderType.Email, + name: null as string, + description: null as string, + priority: 0, + sort: 6, + premium: false, + }, + [TwoFactorProviderType.WebAuthn]: { + type: TwoFactorProviderType.WebAuthn, + name: null as string, + description: null as string, + priority: 4, + sort: 5, + premium: true, + }, + }; + +export class TwoFactorService implements TwoFactorServiceAbstraction { + private twoFactorProvidersData: Map; + private selectedTwoFactorProviderType: TwoFactorProviderType = null; + + constructor( + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService + ) {} + + init() { + TwoFactorProviders[TwoFactorProviderType.Email].name = this.i18nService.t("emailTitle"); + TwoFactorProviders[TwoFactorProviderType.Email].description = this.i18nService.t("emailDesc"); + + TwoFactorProviders[TwoFactorProviderType.Authenticator].name = + this.i18nService.t("authenticatorAppTitle"); + TwoFactorProviders[TwoFactorProviderType.Authenticator].description = + this.i18nService.t("authenticatorAppDesc"); + + TwoFactorProviders[TwoFactorProviderType.Duo].description = this.i18nService.t("duoDesc"); + + TwoFactorProviders[TwoFactorProviderType.OrganizationDuo].name = + "Duo (" + this.i18nService.t("organization") + ")"; + TwoFactorProviders[TwoFactorProviderType.OrganizationDuo].description = + this.i18nService.t("duoOrganizationDesc"); + + TwoFactorProviders[TwoFactorProviderType.WebAuthn].name = this.i18nService.t("webAuthnTitle"); + TwoFactorProviders[TwoFactorProviderType.WebAuthn].description = + this.i18nService.t("webAuthnDesc"); + + TwoFactorProviders[TwoFactorProviderType.Yubikey].name = this.i18nService.t("yubiKeyTitle"); + TwoFactorProviders[TwoFactorProviderType.Yubikey].description = + this.i18nService.t("yubiKeyDesc"); + } + + getSupportedProviders(win: Window): TwoFactorProviderDetails[] { + const providers: any[] = []; + if (this.twoFactorProvidersData == null) { + return providers; + } + + if ( + this.twoFactorProvidersData.has(TwoFactorProviderType.OrganizationDuo) && + this.platformUtilsService.supportsDuo() + ) { + providers.push(TwoFactorProviders[TwoFactorProviderType.OrganizationDuo]); + } + + if (this.twoFactorProvidersData.has(TwoFactorProviderType.Authenticator)) { + providers.push(TwoFactorProviders[TwoFactorProviderType.Authenticator]); + } + + if (this.twoFactorProvidersData.has(TwoFactorProviderType.Yubikey)) { + providers.push(TwoFactorProviders[TwoFactorProviderType.Yubikey]); + } + + if ( + this.twoFactorProvidersData.has(TwoFactorProviderType.Duo) && + this.platformUtilsService.supportsDuo() + ) { + providers.push(TwoFactorProviders[TwoFactorProviderType.Duo]); + } + + if ( + this.twoFactorProvidersData.has(TwoFactorProviderType.WebAuthn) && + this.platformUtilsService.supportsWebAuthn(win) + ) { + providers.push(TwoFactorProviders[TwoFactorProviderType.WebAuthn]); + } + + if (this.twoFactorProvidersData.has(TwoFactorProviderType.Email)) { + providers.push(TwoFactorProviders[TwoFactorProviderType.Email]); + } + + return providers; + } + + getDefaultProvider(webAuthnSupported: boolean): TwoFactorProviderType { + if (this.twoFactorProvidersData == null) { + return null; + } + + if ( + this.selectedTwoFactorProviderType != null && + this.twoFactorProvidersData.has(this.selectedTwoFactorProviderType) + ) { + return this.selectedTwoFactorProviderType; + } + + let providerType: TwoFactorProviderType = null; + let providerPriority = -1; + this.twoFactorProvidersData.forEach((_value, type) => { + const provider = (TwoFactorProviders as any)[type]; + if (provider != null && provider.priority > providerPriority) { + if (type === TwoFactorProviderType.WebAuthn && !webAuthnSupported) { + return; + } + + providerType = type; + providerPriority = provider.priority; + } + }); + + return providerType; + } + + setSelectedProvider(type: TwoFactorProviderType) { + this.selectedTwoFactorProviderType = type; + } + + clearSelectedProvider() { + this.selectedTwoFactorProviderType = null; + } + + setProviders(response: IdentityTwoFactorResponse) { + this.twoFactorProvidersData = response.twoFactorProviders2; + } + + clearProviders() { + this.twoFactorProvidersData = null; + } + + getProviders() { + return this.twoFactorProvidersData; + } +} diff --git a/jslib/common/src/services/userVerification.service.ts b/jslib/common/src/services/userVerification.service.ts new file mode 100644 index 00000000..65207ac0 --- /dev/null +++ b/jslib/common/src/services/userVerification.service.ts @@ -0,0 +1,88 @@ +import { ApiService } from "../abstractions/api.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { UserVerificationService as UserVerificationServiceAbstraction } from "../abstractions/userVerification.service"; +import { VerificationType } from "../enums/verificationType"; +import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest"; +import { SecretVerificationRequest } from "../models/request/secretVerificationRequest"; +import { Verification } from "../types/verification"; + +/** + * Used for general-purpose user verification throughout the app. + * Use it to verify the input collected by UserVerificationComponent. + */ +export class UserVerificationService implements UserVerificationServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private i18nService: I18nService, + private apiService: ApiService + ) {} + + /** + * Create a new request model to be used for server-side verification + * @param verification User-supplied verification data (Master Password or OTP) + * @param requestClass The request model to create + * @param alreadyHashed Whether the master password is already hashed + */ + async buildRequest( + verification: Verification, + requestClass?: new () => T, + alreadyHashed?: boolean + ) { + this.validateInput(verification); + + const request = + requestClass != null ? new requestClass() : (new SecretVerificationRequest() as T); + + if (verification.type === VerificationType.OTP) { + request.otp = verification.secret; + } else { + request.masterPasswordHash = alreadyHashed + ? verification.secret + : await this.cryptoService.hashPassword(verification.secret, null); + } + + return request; + } + + /** + * Used to verify the Master Password client-side, or send the OTP to the server for verification (with no other data) + * Generally used for client-side verification only. + * @param verification User-supplied verification data (Master Password or OTP) + */ + async verifyUser(verification: Verification): Promise { + this.validateInput(verification); + + if (verification.type === VerificationType.OTP) { + const request = new VerifyOTPRequest(verification.secret); + try { + await this.apiService.postAccountVerifyOTP(request); + } catch (e) { + throw new Error(this.i18nService.t("invalidVerificationCode")); + } + } else { + const passwordValid = await this.cryptoService.compareAndUpdateKeyHash( + verification.secret, + null + ); + if (!passwordValid) { + throw new Error(this.i18nService.t("invalidMasterPassword")); + } + } + return true; + } + + async requestOTP() { + await this.apiService.postAccountRequestOTP(); + } + + private validateInput(verification: Verification) { + if (verification?.secret == null || verification.secret === "") { + if (verification.type === VerificationType.OTP) { + throw new Error(this.i18nService.t("verificationCodeRequired")); + } else { + throw new Error(this.i18nService.t("masterPassRequired")); + } + } + } +} diff --git a/jslib/common/src/services/usernameGeneration.service.ts b/jslib/common/src/services/usernameGeneration.service.ts new file mode 100644 index 00000000..e4201d6e --- /dev/null +++ b/jslib/common/src/services/usernameGeneration.service.ts @@ -0,0 +1,128 @@ +import { CryptoService } from "../abstractions/crypto.service"; +import { StateService } from "../abstractions/state.service"; +import { UsernameGenerationService as BaseUsernameGenerationService } from "../abstractions/usernameGeneration.service"; +import { EEFLongWordList } from "../misc/wordlist"; + +const DefaultOptions = { + type: "word", + wordCapitalize: true, + wordIncludeNumber: true, + subaddressType: "random", + catchallType: "random", +}; + +export class UsernameGenerationService implements BaseUsernameGenerationService { + constructor(private cryptoService: CryptoService, private stateService: StateService) {} + + generateUsername(options: any): Promise { + if (options.type === "catchall") { + return this.generateCatchall(options); + } else if (options.type === "subaddress") { + return this.generateSubaddress(options); + } else if (options.type === "forwarded") { + return this.generateSubaddress(options); + } else { + return this.generateWord(options); + } + } + + async generateWord(options: any): Promise { + const o = Object.assign({}, DefaultOptions, options); + + if (o.wordCapitalize == null) { + o.wordCapitalize = true; + } + if (o.wordIncludeNumber == null) { + o.wordIncludeNumber = true; + } + + const wordIndex = await this.cryptoService.randomNumber(0, EEFLongWordList.length - 1); + let word = EEFLongWordList[wordIndex]; + if (o.wordCapitalize) { + word = word.charAt(0).toUpperCase() + word.slice(1); + } + if (o.wordIncludeNumber) { + const num = await this.cryptoService.randomNumber(1, 9999); + word = word + this.zeroPad(num.toString(), 4); + } + return word; + } + + async generateSubaddress(options: any): Promise { + const o = Object.assign({}, DefaultOptions, options); + + const subaddressEmail = o.subaddressEmail; + if (subaddressEmail == null || subaddressEmail.length < 3) { + return o.subaddressEmail; + } + const atIndex = subaddressEmail.indexOf("@"); + if (atIndex < 1 || atIndex >= subaddressEmail.length - 1) { + return subaddressEmail; + } + if (o.subaddressType == null) { + o.subaddressType = "random"; + } + + const emailBeginning = subaddressEmail.substr(0, atIndex); + const emailEnding = subaddressEmail.substr(atIndex + 1, subaddressEmail.length); + + let subaddressString = ""; + if (o.subaddressType === "random") { + subaddressString = await this.randomString(8); + } else if (o.subaddressType === "website-name") { + subaddressString = o.website; + } + return emailBeginning + "+" + subaddressString + "@" + emailEnding; + } + + async generateCatchall(options: any): Promise { + const o = Object.assign({}, DefaultOptions, options); + + if (o.catchallDomain == null || o.catchallDomain === "") { + return null; + } + if (o.catchallType == null) { + o.catchallType = "random"; + } + + let startString = ""; + if (o.catchallType === "random") { + startString = await this.randomString(8); + } else if (o.catchallType === "website-name") { + startString = o.website; + } + return startString + "@" + o.catchallDomain; + } + + async getOptions(): Promise { + let options = await this.stateService.getUsernameGenerationOptions(); + if (options == null) { + options = Object.assign({}, DefaultOptions); + } else { + options = Object.assign({}, DefaultOptions, options); + } + await this.stateService.setUsernameGenerationOptions(options); + return options; + } + + async saveOptions(options: any) { + await this.stateService.setUsernameGenerationOptions(options); + } + + private async randomString(length: number) { + let str = ""; + const charSet = "abcdefghijklmnopqrstuvwxyz1234567890"; + for (let i = 0; i < length; i++) { + const randomCharIndex = await this.cryptoService.randomNumber(0, charSet.length - 1); + str += charSet.charAt(randomCharIndex); + } + return str; + } + + // ref: https://stackoverflow.com/a/10073788 + private zeroPad(number: string, width: number) { + return number.length >= width + ? number + : new Array(width - number.length + 1).join("0") + number; + } +} diff --git a/jslib/common/src/services/vaultTimeout.service.ts b/jslib/common/src/services/vaultTimeout.service.ts new file mode 100644 index 00000000..a6513d65 --- /dev/null +++ b/jslib/common/src/services/vaultTimeout.service.ts @@ -0,0 +1,222 @@ +import { CipherService } from "../abstractions/cipher.service"; +import { CollectionService } from "../abstractions/collection.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FolderService } from "../abstractions/folder.service"; +import { KeyConnectorService } from "../abstractions/keyConnector.service"; +import { MessagingService } from "../abstractions/messaging.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { PolicyService } from "../abstractions/policy.service"; +import { SearchService } from "../abstractions/search.service"; +import { StateService } from "../abstractions/state.service"; +import { TokenService } from "../abstractions/token.service"; +import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "../abstractions/vaultTimeout.service"; +import { KeySuffixOptions } from "../enums/keySuffixOptions"; +import { PolicyType } from "../enums/policyType"; + +export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { + private inited = false; + + constructor( + private cipherService: CipherService, + private folderService: FolderService, + private collectionService: CollectionService, + private cryptoService: CryptoService, + protected platformUtilsService: PlatformUtilsService, + private messagingService: MessagingService, + private searchService: SearchService, + private tokenService: TokenService, + private policyService: PolicyService, + private keyConnectorService: KeyConnectorService, + private stateService: StateService, + private lockedCallback: (userId?: string) => Promise = null, + private loggedOutCallback: (userId?: string) => Promise = null + ) {} + + init(checkOnInterval: boolean) { + if (this.inited) { + return; + } + + this.inited = true; + if (checkOnInterval) { + this.startCheck(); + } + } + + startCheck() { + this.checkVaultTimeout(); + setInterval(() => this.checkVaultTimeout(), 10 * 1000); // check every 10 seconds + } + + // Keys aren't stored for a device that is locked or logged out. + async isLocked(userId?: string): Promise { + const neverLock = + (await this.cryptoService.hasKeyStored(KeySuffixOptions.Auto, userId)) && + !(await this.stateService.getEverBeenUnlocked({ userId: userId })); + if (neverLock) { + // TODO: This also _sets_ the key so when we check memory in the next line it finds a key. + // We should refactor here. + await this.cryptoService.getKey(KeySuffixOptions.Auto, userId); + } + + return !(await this.cryptoService.hasKeyInMemory(userId)); + } + + async checkVaultTimeout(): Promise { + if (await this.platformUtilsService.isViewOpen()) { + return; + } + + for (const userId in this.stateService.accounts.getValue()) { + if (userId != null && (await this.shouldLock(userId))) { + await this.executeTimeoutAction(userId); + } + } + } + + async lock(allowSoftLock = false, userId?: string): Promise { + const authed = await this.stateService.getIsAuthenticated({ userId: userId }); + if (!authed) { + return; + } + + if (await this.keyConnectorService.getUsesKeyConnector()) { + const pinSet = await this.isPinLockSet(); + const pinLock = + (pinSet[0] && (await this.stateService.getDecryptedPinProtected()) != null) || pinSet[1]; + + if (!pinLock && !(await this.isBiometricLockSet())) { + await this.logOut(userId); + } + } + + if (userId == null || userId === (await this.stateService.getUserId())) { + this.searchService.clearIndex(); + } + + await this.stateService.setEverBeenUnlocked(true, { userId: userId }); + await this.stateService.setBiometricLocked(true, { userId: userId }); + await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId }); + + await this.cryptoService.clearKey(false, userId); + await this.cryptoService.clearOrgKeys(true, userId); + await this.cryptoService.clearKeyPair(true, userId); + await this.cryptoService.clearEncKey(true, userId); + + await this.folderService.clearCache(userId); + await this.cipherService.clearCache(userId); + await this.collectionService.clearCache(userId); + + this.messagingService.send("locked", { userId: userId }); + + if (this.lockedCallback != null) { + await this.lockedCallback(userId); + } + } + + async logOut(userId?: string): Promise { + if (this.loggedOutCallback != null) { + await this.loggedOutCallback(userId); + } + } + + async setVaultTimeoutOptions(timeout: number, action: string): Promise { + await this.stateService.setVaultTimeout(timeout); + + // We swap these tokens from being on disk for lock actions, and in memory for logout actions + // Get them here to set them to their new location after changing the timeout action and clearing if needed + const token = await this.tokenService.getToken(); + const refreshToken = await this.tokenService.getRefreshToken(); + const clientId = await this.tokenService.getClientId(); + const clientSecret = await this.tokenService.getClientSecret(); + + const currentAction = await this.stateService.getVaultTimeoutAction(); + if ((timeout != null || timeout === 0) && action === "logOut" && action !== currentAction) { + // if we have a vault timeout and the action is log out, reset tokens + await this.tokenService.clearToken(); + } + + await this.stateService.setVaultTimeoutAction(action); + + await this.tokenService.setToken(token); + await this.tokenService.setRefreshToken(refreshToken); + await this.tokenService.setClientId(clientId); + await this.tokenService.setClientSecret(clientSecret); + + await this.cryptoService.toggleKey(); + } + + async isPinLockSet(): Promise<[boolean, boolean]> { + const protectedPin = await this.stateService.getProtectedPin(); + const pinProtectedKey = await this.stateService.getEncryptedPinProtected(); + return [protectedPin != null, pinProtectedKey != null]; + } + + async isBiometricLockSet(): Promise { + return await this.stateService.getBiometricUnlock(); + } + + async getVaultTimeout(userId?: string): Promise { + const vaultTimeout = await this.stateService.getVaultTimeout({ userId: userId }); + + if ( + await this.policyService.policyAppliesToUser(PolicyType.MaximumVaultTimeout, null, userId) + ) { + const policy = await this.policyService.getAll(PolicyType.MaximumVaultTimeout, userId); + // Remove negative values, and ensure it's smaller than maximum allowed value according to policy + let timeout = Math.min(vaultTimeout, policy[0].data.minutes); + + if (vaultTimeout == null || timeout < 0) { + timeout = policy[0].data.minutes; + } + + // We really shouldn't need to set the value here, but multiple services relies on this value being correct. + if (vaultTimeout !== timeout) { + await this.stateService.setVaultTimeout(timeout, { userId: userId }); + } + + return timeout; + } + + return vaultTimeout; + } + + async clear(userId?: string): Promise { + await this.stateService.setEverBeenUnlocked(false, { userId: userId }); + await this.stateService.setDecryptedPinProtected(null, { userId: userId }); + await this.stateService.setProtectedPin(null, { userId: userId }); + } + + private async isLoggedOut(userId?: string): Promise { + return !(await this.stateService.getIsAuthenticated({ userId: userId })); + } + + private async shouldLock(userId: string): Promise { + if (await this.isLoggedOut(userId)) { + return false; + } + + if (await this.isLocked(userId)) { + return false; + } + + const vaultTimeout = await this.getVaultTimeout(userId); + if (vaultTimeout == null || vaultTimeout < 0) { + return false; + } + + const lastActive = await this.stateService.getLastActive({ userId: userId }); + if (lastActive == null) { + return false; + } + + const vaultTimeoutSeconds = vaultTimeout * 60; + const diffSeconds = (new Date().getTime() - lastActive) / 1000; + return diffSeconds >= vaultTimeoutSeconds; + } + + private async executeTimeoutAction(userId: string): Promise { + const timeoutAction = await this.stateService.getVaultTimeoutAction({ userId: userId }); + timeoutAction === "logOut" ? await this.logOut(userId) : await this.lock(true, userId); + } +} diff --git a/jslib/common/src/services/webCryptoFunction.service.ts b/jslib/common/src/services/webCryptoFunction.service.ts new file mode 100644 index 00000000..b863f226 --- /dev/null +++ b/jslib/common/src/services/webCryptoFunction.service.ts @@ -0,0 +1,356 @@ +import * as forge from "node-forge"; + +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { Utils } from "../misc/utils"; +import { DecryptParameters } from "../models/domain/decryptParameters"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; + +export class WebCryptoFunctionService implements CryptoFunctionService { + private crypto: Crypto; + private subtle: SubtleCrypto; + + constructor(win: Window) { + this.crypto = typeof win.crypto !== "undefined" ? win.crypto : null; + this.subtle = + !!this.crypto && typeof win.crypto.subtle !== "undefined" ? win.crypto.subtle : null; + } + + async pbkdf2( + password: string | ArrayBuffer, + salt: string | ArrayBuffer, + algorithm: "sha256" | "sha512", + iterations: number + ): Promise { + const wcLen = algorithm === "sha256" ? 256 : 512; + const passwordBuf = this.toBuf(password); + const saltBuf = this.toBuf(salt); + + const pbkdf2Params: Pbkdf2Params = { + name: "PBKDF2", + salt: saltBuf, + iterations: iterations, + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + + const impKey = await this.subtle.importKey( + "raw", + passwordBuf, + { name: "PBKDF2" } as any, + false, + ["deriveBits"] + ); + return await this.subtle.deriveBits(pbkdf2Params, impKey, wcLen); + } + + async hkdf( + ikm: ArrayBuffer, + salt: string | ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ): Promise { + const saltBuf = this.toBuf(salt); + const infoBuf = this.toBuf(info); + + const hkdfParams: HkdfParams = { + name: "HKDF", + salt: saltBuf, + info: infoBuf, + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + + const impKey = await this.subtle.importKey("raw", ikm, { name: "HKDF" } as any, false, [ + "deriveBits", + ]); + return await this.subtle.deriveBits(hkdfParams as any, impKey, outputByteSize * 8); + } + + // ref: https://tools.ietf.org/html/rfc5869 + async hkdfExpand( + prk: ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ): Promise { + const hashLen = algorithm === "sha256" ? 32 : 64; + if (outputByteSize > 255 * hashLen) { + throw new Error("outputByteSize is too large."); + } + const prkArr = new Uint8Array(prk); + if (prkArr.length < hashLen) { + throw new Error("prk is too small."); + } + const infoBuf = this.toBuf(info); + const infoArr = new Uint8Array(infoBuf); + let runningOkmLength = 0; + let previousT = new Uint8Array(0); + const n = Math.ceil(outputByteSize / hashLen); + const okm = new Uint8Array(n * hashLen); + for (let i = 0; i < n; i++) { + const t = new Uint8Array(previousT.length + infoArr.length + 1); + t.set(previousT); + t.set(infoArr, previousT.length); + t.set([i + 1], t.length - 1); + previousT = new Uint8Array(await this.hmac(t.buffer, prk, algorithm)); + okm.set(previousT, runningOkmLength); + runningOkmLength += previousT.length; + if (runningOkmLength >= outputByteSize) { + break; + } + } + return okm.slice(0, outputByteSize).buffer; + } + + async hash( + value: string | ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" | "md5" + ): Promise { + if (algorithm === "md5") { + const md = algorithm === "md5" ? forge.md.md5.create() : forge.md.sha1.create(); + const valueBytes = this.toByteString(value); + md.update(valueBytes, "raw"); + return Utils.fromByteStringToArray(md.digest().data).buffer; + } + + const valueBuf = this.toBuf(value); + return await this.subtle.digest({ name: this.toWebCryptoAlgorithm(algorithm) }, valueBuf); + } + + async hmac( + value: ArrayBuffer, + key: ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" + ): Promise { + const signingAlgorithm = { + name: "HMAC", + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + + const impKey = await this.subtle.importKey("raw", key, signingAlgorithm, false, ["sign"]); + return await this.subtle.sign(signingAlgorithm, impKey, value); + } + + // Safely compare two values in a way that protects against timing attacks (Double HMAC Verification). + // ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/ + // ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy + async compare(a: ArrayBuffer, b: ArrayBuffer): Promise { + const macKey = await this.randomBytes(32); + const signingAlgorithm = { + name: "HMAC", + hash: { name: "SHA-256" }, + }; + const impKey = await this.subtle.importKey("raw", macKey, signingAlgorithm, false, ["sign"]); + const mac1 = await this.subtle.sign(signingAlgorithm, impKey, a); + const mac2 = await this.subtle.sign(signingAlgorithm, impKey, b); + + if (mac1.byteLength !== mac2.byteLength) { + return false; + } + + const arr1 = new Uint8Array(mac1); + const arr2 = new Uint8Array(mac2); + for (let i = 0; i < arr2.length; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + + return true; + } + + hmacFast(value: string, key: string, algorithm: "sha1" | "sha256" | "sha512"): Promise { + const hmac = forge.hmac.create(); + hmac.start(algorithm, key); + hmac.update(value); + const bytes = hmac.digest().getBytes(); + return Promise.resolve(bytes); + } + + async compareFast(a: string, b: string): Promise { + const rand = await this.randomBytes(32); + const bytes = new Uint32Array(rand); + const buffer = forge.util.createBuffer(); + for (let i = 0; i < bytes.length; i++) { + buffer.putInt32(bytes[i]); + } + const macKey = buffer.getBytes(); + + const hmac = forge.hmac.create(); + hmac.start("sha256", macKey); + hmac.update(a); + const mac1 = hmac.digest().getBytes(); + + hmac.start(null, null); + hmac.update(b); + const mac2 = hmac.digest().getBytes(); + + const equals = mac1 === mac2; + return equals; + } + + async aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const impKey = await this.subtle.importKey("raw", key, { name: "AES-CBC" } as any, false, [ + "encrypt", + ]); + return await this.subtle.encrypt({ name: "AES-CBC", iv: iv }, impKey, data); + } + + aesDecryptFastParameters( + data: string, + iv: string, + mac: string, + key: SymmetricCryptoKey + ): DecryptParameters { + const p = new DecryptParameters(); + if (key.meta != null) { + p.encKey = key.meta.encKeyByteString; + p.macKey = key.meta.macKeyByteString; + } + + if (p.encKey == null) { + p.encKey = forge.util.decode64(key.encKeyB64); + } + p.data = forge.util.decode64(data); + p.iv = forge.util.decode64(iv); + p.macData = p.iv + p.data; + if (p.macKey == null && key.macKeyB64 != null) { + p.macKey = forge.util.decode64(key.macKeyB64); + } + if (mac != null) { + p.mac = forge.util.decode64(mac); + } + + // cache byte string keys for later + if (key.meta == null) { + key.meta = {}; + } + if (key.meta.encKeyByteString == null) { + key.meta.encKeyByteString = p.encKey; + } + if (p.macKey != null && key.meta.macKeyByteString == null) { + key.meta.macKeyByteString = p.macKey; + } + + return p; + } + + aesDecryptFast(parameters: DecryptParameters): Promise { + const dataBuffer = forge.util.createBuffer(parameters.data); + const decipher = forge.cipher.createDecipher("AES-CBC", parameters.encKey); + decipher.start({ iv: parameters.iv }); + decipher.update(dataBuffer); + decipher.finish(); + const val = decipher.output.toString(); + return Promise.resolve(val); + } + + async aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const impKey = await this.subtle.importKey("raw", key, { name: "AES-CBC" } as any, false, [ + "decrypt", + ]); + return await this.subtle.decrypt({ name: "AES-CBC", iv: iv }, impKey, data); + } + + async rsaEncrypt( + data: ArrayBuffer, + publicKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ): Promise { + // Note: Edge browser requires that we specify name and hash for both key import and decrypt. + // We cannot use the proper types here. + const rsaParams = { + name: "RSA-OAEP", + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + const impKey = await this.subtle.importKey("spki", publicKey, rsaParams, false, ["encrypt"]); + return await this.subtle.encrypt(rsaParams, impKey, data); + } + + async rsaDecrypt( + data: ArrayBuffer, + privateKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ): Promise { + // Note: Edge browser requires that we specify name and hash for both key import and decrypt. + // We cannot use the proper types here. + const rsaParams = { + name: "RSA-OAEP", + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + const impKey = await this.subtle.importKey("pkcs8", privateKey, rsaParams, false, ["decrypt"]); + return await this.subtle.decrypt(rsaParams, impKey, data); + } + + async rsaExtractPublicKey(privateKey: ArrayBuffer): Promise { + const rsaParams = { + name: "RSA-OAEP", + // Have to specify some algorithm + hash: { name: this.toWebCryptoAlgorithm("sha1") }, + }; + const impPrivateKey = await this.subtle.importKey("pkcs8", privateKey, rsaParams, true, [ + "decrypt", + ]); + const jwkPrivateKey = await this.subtle.exportKey("jwk", impPrivateKey); + const jwkPublicKeyParams = { + kty: "RSA", + e: jwkPrivateKey.e, + n: jwkPrivateKey.n, + alg: "RSA-OAEP", + ext: true, + }; + const impPublicKey = await this.subtle.importKey("jwk", jwkPublicKeyParams, rsaParams, true, [ + "encrypt", + ]); + return await this.subtle.exportKey("spki", impPublicKey); + } + + async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[ArrayBuffer, ArrayBuffer]> { + const rsaParams = { + name: "RSA-OAEP", + modulusLength: length, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537 + // Have to specify some algorithm + hash: { name: this.toWebCryptoAlgorithm("sha1") }, + }; + const keyPair = (await this.subtle.generateKey(rsaParams, true, [ + "encrypt", + "decrypt", + ])) as CryptoKeyPair; + const publicKey = await this.subtle.exportKey("spki", keyPair.publicKey); + const privateKey = await this.subtle.exportKey("pkcs8", keyPair.privateKey); + return [publicKey, privateKey]; + } + + randomBytes(length: number): Promise { + const arr = new Uint8Array(length); + this.crypto.getRandomValues(arr); + return Promise.resolve(arr.buffer); + } + + private toBuf(value: string | ArrayBuffer): ArrayBuffer { + let buf: ArrayBuffer; + if (typeof value === "string") { + buf = Utils.fromUtf8ToArray(value).buffer; + } else { + buf = value; + } + return buf; + } + + private toByteString(value: string | ArrayBuffer): string { + let bytes: string; + if (typeof value === "string") { + bytes = forge.util.encodeUtf8(value); + } else { + bytes = Utils.fromBufferToByteString(value); + } + return bytes; + } + + private toWebCryptoAlgorithm(algorithm: "sha1" | "sha256" | "sha512" | "md5"): string { + if (algorithm === "md5") { + throw new Error("MD5 is not supported in WebCrypto."); + } + return algorithm === "sha1" ? "SHA-1" : algorithm === "sha256" ? "SHA-256" : "SHA-512"; + } +} diff --git a/jslib/common/src/types/verification.ts b/jslib/common/src/types/verification.ts new file mode 100644 index 00000000..07ca4bbf --- /dev/null +++ b/jslib/common/src/types/verification.ts @@ -0,0 +1,6 @@ +import { VerificationType } from "../enums/verificationType"; + +export type Verification = { + type: VerificationType; + secret: string; +}; diff --git a/jslib/common/tsconfig.json b/jslib/common/tsconfig.json new file mode 100644 index 00000000..0d578efa --- /dev/null +++ b/jslib/common/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../shared/tsconfig", + "compilerOptions": { + "paths": { + "jslib-common/*": ["./src/*"] + } + }, + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/common/tsconfig.spec.json b/jslib/common/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/common/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/jslib/electron/jest.config.js b/jslib/electron/jest.config.js new file mode 100644 index 00000000..8d59aeaf --- /dev/null +++ b/jslib/electron/jest.config.js @@ -0,0 +1,16 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + preset: "ts-jest", + testEnvironment: "jsdom", + testMatch: ["**/+(*.)+(spec).+(ts)"], + setupFilesAfterEnv: ["/spec/test.ts"], + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/jslib/electron/package-lock.json b/jslib/electron/package-lock.json new file mode 100644 index 00000000..608d0561 --- /dev/null +++ b/jslib/electron/package-lock.json @@ -0,0 +1,2393 @@ +{ + "name": "@bitwarden/jslib-electron", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib-electron", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "electron": "16.1.0", + "electron-log": "4.4.6", + "electron-store": "8.0.1", + "electron-updater": "5.0.0" + }, + "devDependencies": { + "@types/node": "^16.11.12", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "../common": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@bitwarden/jslib-common": { + "resolved": "../common", + "link": true + }, + "node_modules/@electron/get": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=8.6" + }, + "optionalDependencies": { + "global-agent": "^3.0.0", + "global-tunnel-ng": "^2.7.1" + } + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==" + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/builder-util-runtime": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.0.0.tgz", + "integrity": "sha512-SkpEtSmTkREDHRJnxKEv43aAYp8sYWY8fxYBhGLBLOBIRXeaIp6Kv3lBgSD7uR8jQtC7CA659sqJrpSV6zNvSA==", + "dependencies": { + "debug": "^4.3.2", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/conf": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.1.1.tgz", + "integrity": "sha512-z2civwq/k8TMYtcn3SVP0Peso4otIWnHtcTuHhQ0zDZDdP4NTxqEc8owfkz4zBsdMYdn/LFcE+ZhbCeqkhtq3Q==", + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dependencies": { + "mimic-fn": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "optional": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "optional": true + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "node_modules/electron": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-16.1.0.tgz", + "integrity": "sha512-G4fpHmE3sgd497e0zEier/AmZ4fyReX8ozYAl468+FaI5kb44+69igRHQwRUtmPzv+fCn/Jm4wJQPfLe60WmUQ==", + "hasInstallScript": true, + "dependencies": { + "@electron/get": "^1.13.0", + "@types/node": "^14.6.2", + "extract-zip": "^1.0.3" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 8.6" + } + }, + "node_modules/electron-log": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-4.4.6.tgz", + "integrity": "sha512-nirYgRdY+F+vclr8ijdwy2vW03IzFpDHTaKNWu76dEN21Y76+smcES5knS7cgHUUB0qNLOi8vZO36taakjbSXA==" + }, + "node_modules/electron-store": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.1.tgz", + "integrity": "sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==", + "dependencies": { + "conf": "^10.0.3", + "type-fest": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-updater": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.0.0.tgz", + "integrity": "sha512-SC3sw92ewjrJFZIJamVOHqxW3yzFin/Q/Swf2FZodqm9xd4s8hCbPCfptpD/xBIcvQmAv2BAggbprwWq/fyp6w==", + "dependencies": { + "@types/semver": "^7.3.6", + "builder-util-runtime": "9.0.0", + "fs-extra": "^10.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.3.5" + } + }, + "node_modules/electron-updater/node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-updater/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-updater/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-updater/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron/node_modules/@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dependencies": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/extract-zip/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "dependencies": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "optional": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "optional": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "dependencies": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + }, + "dependencies": { + "@bitwarden/jslib-common": { + "version": "file:../common", + "requires": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zxcvbn": "^4.4.2" + } + }, + "@electron/get": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^3.0.0", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==" + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "builder-util-runtime": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.0.0.tgz", + "integrity": "sha512-SkpEtSmTkREDHRJnxKEv43aAYp8sYWY8fxYBhGLBLOBIRXeaIp6Kv3lBgSD7uR8jQtC7CA659sqJrpSV6zNvSA==", + "requires": { + "debug": "^4.3.2", + "sax": "^1.2.4" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "conf": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.1.1.tgz", + "integrity": "sha512-z2civwq/k8TMYtcn3SVP0Peso4otIWnHtcTuHhQ0zDZDdP4NTxqEc8owfkz4zBsdMYdn/LFcE+ZhbCeqkhtq3Q==", + "requires": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "requires": { + "mimic-fn": "^3.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "optional": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "optional": true + }, + "dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "electron": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-16.1.0.tgz", + "integrity": "sha512-G4fpHmE3sgd497e0zEier/AmZ4fyReX8ozYAl468+FaI5kb44+69igRHQwRUtmPzv+fCn/Jm4wJQPfLe60WmUQ==", + "requires": { + "@electron/get": "^1.13.0", + "@types/node": "^14.6.2", + "extract-zip": "^1.0.3" + }, + "dependencies": { + "@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" + } + } + }, + "electron-log": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-4.4.6.tgz", + "integrity": "sha512-nirYgRdY+F+vclr8ijdwy2vW03IzFpDHTaKNWu76dEN21Y76+smcES5knS7cgHUUB0qNLOi8vZO36taakjbSXA==" + }, + "electron-store": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.1.tgz", + "integrity": "sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==", + "requires": { + "conf": "^10.0.3", + "type-fest": "^1.0.2" + } + }, + "electron-updater": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.0.0.tgz", + "integrity": "sha512-SC3sw92ewjrJFZIJamVOHqxW3yzFin/Q/Swf2FZodqm9xd4s8hCbPCfptpD/xBIcvQmAv2BAggbprwWq/fyp6w==", + "requires": { + "@types/semver": "^7.3.6", + "builder-util-runtime": "9.0.0", + "fs-extra": "^10.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.3.5" + }, + "dependencies": { + "fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "optional": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "optional": true + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "optional": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "optional": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "requires": { + "escape-string-regexp": "^4.0.0" + } + }, + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + } + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "optional": true + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "requires": { + "type-fest": "^0.13.1" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true + } + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "requires": { + "debug": "^4.1.0" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true + }, + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/jslib/electron/package.json b/jslib/electron/package.json new file mode 100644 index 00000000..892446d5 --- /dev/null +++ b/jslib/electron/package.json @@ -0,0 +1,32 @@ +{ + "name": "@bitwarden/jslib-electron", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + }, + "devDependencies": { + "@types/node": "^16.11.12", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "electron": "16.1.0", + "electron-log": "4.4.6", + "electron-store": "8.0.1", + "electron-updater": "5.0.0" + } +} diff --git a/jslib/electron/spec/services/electronLog.service.spec.ts b/jslib/electron/spec/services/electronLog.service.spec.ts new file mode 100644 index 00000000..5952da43 --- /dev/null +++ b/jslib/electron/spec/services/electronLog.service.spec.ts @@ -0,0 +1,9 @@ +import { ElectronLogService } from "jslib-electron/services/electronLog.service"; + +describe("ElectronLogService", () => { + it("sets dev based on electron method", () => { + process.env.ELECTRON_IS_DEV = "1"; + const logService = new ElectronLogService(); + expect(logService).toEqual(expect.objectContaining({ isDev: true }) as any); + }); +}); diff --git a/jslib/electron/spec/test.ts b/jslib/electron/spec/test.ts new file mode 100644 index 00000000..e69de29b diff --git a/jslib/electron/spec/utils.spec.ts b/jslib/electron/spec/utils.spec.ts new file mode 100644 index 00000000..f9e95a2d --- /dev/null +++ b/jslib/electron/spec/utils.spec.ts @@ -0,0 +1,27 @@ +import { cleanUserAgent } from "jslib-electron/utils"; + +const expectedUserAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${process.versions.chrome} Safari/537.36`; + +describe("cleanUserAgent", () => { + it("cleans mac agent", () => { + const initialMacAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 11_6_0) AppleWebKit/537.36 (KHTML, like Gecko) Bitwarden/${process.version} Chrome/${process.versions.chrome} Electron/${process.versions.electron} Safari/537.36`; + expect(cleanUserAgent(initialMacAgent)).toEqual(expectedUserAgent); + }); + + it("cleans windows agent", () => { + const initialWindowsAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Bitwarden/${process.version} Chrome/${process.versions.chrome} Electron/${process.versions.electron} Safari/537.36`; + expect(cleanUserAgent(initialWindowsAgent)).toEqual(expectedUserAgent); + }); + + it("cleans linux agent", () => { + const initialWindowsAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Bitwarden/${process.version} Chrome/${process.versions.chrome} Electron/${process.versions.electron} Safari/537.36`; + expect(cleanUserAgent(initialWindowsAgent)).toEqual(expectedUserAgent); + }); + + it("does not change version numbers", () => { + const expected = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36`; + const initialAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Bitwarden/1.28.3 Chrome/87.0.4280.141 Electron/11.4.5 Safari/537.36`; + + expect(cleanUserAgent(initialAgent)).toEqual(expected); + }); +}); diff --git a/jslib/electron/src/baseMenu.ts b/jslib/electron/src/baseMenu.ts new file mode 100644 index 00000000..09d47f7e --- /dev/null +++ b/jslib/electron/src/baseMenu.ts @@ -0,0 +1,225 @@ +import { Menu, MenuItemConstructorOptions } from "electron"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +import { WindowMain } from "./window.main"; + +export class BaseMenu { + protected editMenuItemOptions: MenuItemConstructorOptions; + protected viewSubMenuItemOptions: MenuItemConstructorOptions[]; + protected windowMenuItemOptions: MenuItemConstructorOptions; + protected macAppMenuItemOptions: MenuItemConstructorOptions[]; + protected macWindowSubmenuOptions: MenuItemConstructorOptions[]; + + constructor(protected i18nService: I18nService, protected windowMain: WindowMain) {} + + protected initProperties() { + this.editMenuItemOptions = { + label: this.i18nService.t("edit"), + submenu: [ + { + label: this.i18nService.t("undo"), + role: "undo", + }, + { + label: this.i18nService.t("redo"), + role: "redo", + }, + { type: "separator" }, + { + label: this.i18nService.t("cut"), + role: "cut", + }, + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ], + }; + + this.viewSubMenuItemOptions = [ + { + label: this.i18nService.t("zoomIn"), + role: "zoomIn", + accelerator: "CmdOrCtrl+=", + }, + { + label: this.i18nService.t("zoomOut"), + role: "zoomOut", + accelerator: "CmdOrCtrl+-", + }, + { + label: this.i18nService.t("resetZoom"), + role: "resetZoom", + accelerator: "CmdOrCtrl+0", + }, + { type: "separator" }, + { + label: this.i18nService.t("toggleFullScreen"), + role: "togglefullscreen", + }, + { type: "separator" }, + { + label: this.i18nService.t("reload"), + role: "forceReload", + }, + { + label: this.i18nService.t("toggleDevTools"), + role: "toggleDevTools", + accelerator: "F12", + }, + ]; + + this.windowMenuItemOptions = { + label: this.i18nService.t("window"), + role: "window", + submenu: [ + { + label: this.i18nService.t("minimize"), + role: "minimize", + }, + { + label: this.i18nService.t("close"), + role: "close", + }, + ], + }; + + if (process.platform === "darwin") { + this.macAppMenuItemOptions = [ + { + label: this.i18nService.t("services"), + role: "services", + submenu: [], + }, + { type: "separator" }, + { + label: this.i18nService.t("hideBitwarden"), + role: "hide", + }, + { + label: this.i18nService.t("hideOthers"), + role: "hideOthers", + }, + { + label: this.i18nService.t("showAll"), + role: "unhide", + }, + { type: "separator" }, + { + label: this.i18nService.t("quitBitwarden"), + role: "quit", + }, + ]; + + this.macWindowSubmenuOptions = [ + { + label: this.i18nService.t("minimize"), + role: "minimize", + }, + { + label: this.i18nService.t("zoom"), + role: "zoom", + }, + { type: "separator" }, + { + label: this.i18nService.t("bringAllToFront"), + role: "front", + }, + { + label: this.i18nService.t("close"), + role: "close", + }, + ]; + } + } + + protected initContextMenu() { + if (this.windowMain.win == null) { + return; + } + + const selectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + const inputMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("undo"), + role: "undo", + }, + { + label: this.i18nService.t("redo"), + role: "redo", + }, + { type: "separator" }, + { + label: this.i18nService.t("cut"), + role: "cut", + enabled: false, + }, + { + label: this.i18nService.t("copy"), + role: "copy", + enabled: false, + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + const inputSelectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("cut"), + role: "cut", + }, + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + this.windowMain.win.webContents.on("context-menu", (e, props) => { + const selected = props.selectionText && props.selectionText.trim() !== ""; + if (props.isEditable && selected) { + inputSelectionMenu.popup({ window: this.windowMain.win }); + } else if (props.isEditable) { + inputMenu.popup({ window: this.windowMain.win }); + } else if (selected) { + selectionMenu.popup({ window: this.windowMain.win }); + } + }); + } +} diff --git a/jslib/electron/src/globals.d.ts b/jslib/electron/src/globals.d.ts new file mode 100644 index 00000000..e69de29b diff --git a/jslib/electron/src/services/electronCrypto.service.ts b/jslib/electron/src/services/electronCrypto.service.ts new file mode 100644 index 00000000..36ea8132 --- /dev/null +++ b/jslib/electron/src/services/electronCrypto.service.ts @@ -0,0 +1,71 @@ +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { KeySuffixOptions } from "jslib-common/enums/keySuffixOptions"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { CryptoService } from "jslib-common/services/crypto.service"; + +export class ElectronCryptoService extends CryptoService { + constructor( + cryptoFunctionService: CryptoFunctionService, + platformUtilService: PlatformUtilsService, + logService: LogService, + stateService: StateService + ) { + super(cryptoFunctionService, platformUtilService, logService, stateService); + } + + async hasKeyStored(keySuffix: KeySuffixOptions): Promise { + await this.upgradeSecurelyStoredKey(); + return super.hasKeyStored(keySuffix); + } + + protected async storeKey(key: SymmetricCryptoKey, userId?: string) { + if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId)) { + await this.stateService.setCryptoMasterKeyAuto(key.keyB64, { userId: userId }); + } else { + this.clearStoredKey(KeySuffixOptions.Auto); + } + + if (await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) { + await this.stateService.setCryptoMasterKeyBiometric(key.keyB64, { userId: userId }); + } else { + this.clearStoredKey(KeySuffixOptions.Biometric); + } + } + + protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string) { + await this.upgradeSecurelyStoredKey(); + return super.retrieveKeyFromStorage(keySuffix, userId); + } + + /** + * @deprecated 4 Jun 2021 This is temporary upgrade method to move from a single shared stored key to + * multiple, unique stored keys for each use, e.g. never logout vs. biometric authentication. + */ + private async upgradeSecurelyStoredKey() { + // attempt key upgrade, but if we fail just delete it. Keys will be stored property upon unlock anyway. + const key = await this.stateService.getCryptoMasterKeyB64(); + + if (key == null) { + return; + } + + try { + if (await this.shouldStoreKey(KeySuffixOptions.Auto)) { + await this.stateService.setCryptoMasterKeyAuto(key); + } + if (await this.shouldStoreKey(KeySuffixOptions.Biometric)) { + await this.stateService.setCryptoMasterKeyBiometric(key); + } + } catch (e) { + this.logService.error( + `Encountered error while upgrading obsolete Bitwarden secure storage item:` + ); + this.logService.error(e); + } + + await this.stateService.setCryptoMasterKeyB64(null); + } +} diff --git a/jslib/electron/src/services/electronLog.service.ts b/jslib/electron/src/services/electronLog.service.ts new file mode 100644 index 00000000..c6195416 --- /dev/null +++ b/jslib/electron/src/services/electronLog.service.ts @@ -0,0 +1,45 @@ +import * as path from "path"; + +import log from "electron-log"; + +import { LogLevelType } from "jslib-common/enums/logLevelType"; +import { ConsoleLogService as BaseLogService } from "jslib-common/services/consoleLog.service"; + +import { isDev } from "../utils"; + +export class ElectronLogService extends BaseLogService { + constructor(protected filter: (level: LogLevelType) => boolean = null, logDir: string = null) { + super(isDev(), filter); + if (log.transports == null) { + return; + } + + log.transports.file.level = "info"; + if (logDir != null) { + log.transports.file.file = path.join(logDir, "app.log"); + } + } + + write(level: LogLevelType, message: string) { + if (this.filter != null && this.filter(level)) { + return; + } + + switch (level) { + case LogLevelType.Debug: + log.debug(message); + break; + case LogLevelType.Info: + log.info(message); + break; + case LogLevelType.Warning: + log.warn(message); + break; + case LogLevelType.Error: + log.error(message); + break; + default: + break; + } + } +} diff --git a/jslib/electron/src/services/electronMainMessaging.service.ts b/jslib/electron/src/services/electronMainMessaging.service.ts new file mode 100644 index 00000000..f79a32b4 --- /dev/null +++ b/jslib/electron/src/services/electronMainMessaging.service.ts @@ -0,0 +1,65 @@ +import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme } from "electron"; + +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { ThemeType } from "jslib-common/enums/themeType"; + +import { RendererMenuItem } from "../utils"; +import { WindowMain } from "../window.main"; + +export class ElectronMainMessagingService implements MessagingService { + constructor(private windowMain: WindowMain, private onMessage: (message: any) => void) { + ipcMain.handle("appVersion", () => { + return app.getVersion(); + }); + + ipcMain.handle("systemTheme", () => { + return nativeTheme.shouldUseDarkColors ? ThemeType.Dark : ThemeType.Light; + }); + + ipcMain.handle("showMessageBox", (event, options) => { + return dialog.showMessageBox(this.windowMain.win, options); + }); + + ipcMain.handle("openContextMenu", (event, options: { menu: RendererMenuItem[] }) => { + return new Promise((resolve) => { + const menu = new Menu(); + options.menu.forEach((m, index) => { + menu.append( + new MenuItem({ + label: m.label, + type: m.type, + click: () => { + resolve(index); + }, + }) + ); + }); + menu.popup({ + window: windowMain.win, + callback: () => { + resolve(-1); + }, + }); + }); + }); + + ipcMain.handle("windowVisible", () => { + return windowMain.win?.isVisible(); + }); + + nativeTheme.on("updated", () => { + windowMain.win?.webContents.send( + "systemThemeUpdated", + nativeTheme.shouldUseDarkColors ? ThemeType.Dark : ThemeType.Light + ); + }); + } + + send(subscriber: string, arg: any = {}) { + const message = Object.assign({}, { command: subscriber }, arg); + this.onMessage(message); + if (this.windowMain.win != null) { + this.windowMain.win.webContents.send("messagingService", message); + } + } +} diff --git a/jslib/electron/src/services/electronPlatformUtils.service.ts b/jslib/electron/src/services/electronPlatformUtils.service.ts new file mode 100644 index 00000000..c80218d2 --- /dev/null +++ b/jslib/electron/src/services/electronPlatformUtils.service.ts @@ -0,0 +1,217 @@ +import { clipboard, ipcRenderer, shell } from "electron"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { ClientType } from "jslib-common/enums/clientType"; +import { DeviceType } from "jslib-common/enums/deviceType"; +import { ThemeType } from "jslib-common/enums/themeType"; + +import { isDev, isMacAppStore } from "../utils"; + +export class ElectronPlatformUtilsService implements PlatformUtilsService { + private clientType: ClientType; + private deviceCache: DeviceType = null; + + constructor( + protected i18nService: I18nService, + private messagingService: MessagingService, + private isDesktopApp: boolean, + private stateService: StateService + ) { + this.clientType = isDesktopApp ? ClientType.Desktop : ClientType.DirectoryConnector; + } + + getDevice(): DeviceType { + if (!this.deviceCache) { + switch (process.platform) { + case "win32": + this.deviceCache = DeviceType.WindowsDesktop; + break; + case "darwin": + this.deviceCache = DeviceType.MacOsDesktop; + break; + case "linux": + default: + this.deviceCache = DeviceType.LinuxDesktop; + break; + } + } + + return this.deviceCache; + } + + getDeviceString(): string { + const device = DeviceType[this.getDevice()].toLowerCase(); + return device.replace("desktop", ""); + } + + getClientType() { + return this.clientType; + } + + isFirefox(): boolean { + return false; + } + + isChrome(): boolean { + return true; + } + + isEdge(): boolean { + return false; + } + + isOpera(): boolean { + return false; + } + + isVivaldi(): boolean { + return false; + } + + isSafari(): boolean { + return false; + } + + isMacAppStore(): boolean { + return isMacAppStore(); + } + + isViewOpen(): Promise { + return Promise.resolve(false); + } + + launchUri(uri: string, options?: any): void { + shell.openExternal(uri); + } + + saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void { + const blob = new Blob([blobData], blobOptions); + const a = win.document.createElement("a"); + a.href = URL.createObjectURL(blob); + a.download = fileName; + win.document.body.appendChild(a); + a.click(); + win.document.body.removeChild(a); + } + + getApplicationVersion(): Promise { + return ipcRenderer.invoke("appVersion"); + } + + // Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349 + // has been merged and an updated electron build is available. + supportsWebAuthn(win: Window): boolean { + return process.platform === "win32"; + } + + supportsDuo(): boolean { + return true; + } + + showToast( + type: "error" | "success" | "warning" | "info", + title: string, + text: string | string[], + options?: any + ): void { + this.messagingService.send("showToast", { + text: text, + title: title, + type: type, + options: options, + }); + } + + async showDialog( + text: string, + title?: string, + confirmText?: string, + cancelText?: string, + type?: string + ): Promise { + const buttons = [confirmText == null ? this.i18nService.t("ok") : confirmText]; + if (cancelText != null) { + buttons.push(cancelText); + } + + const result = await ipcRenderer.invoke("showMessageBox", { + type: type, + title: title, + message: title, + detail: text, + buttons: buttons, + cancelId: buttons.length === 2 ? 1 : null, + defaultId: 0, + noLink: true, + }); + + return Promise.resolve(result.response === 0); + } + + isDev(): boolean { + return isDev(); + } + + isSelfHost(): boolean { + return false; + } + + copyToClipboard(text: string, options?: any): void { + const type = options ? options.type : null; + const clearing = options ? !!options.clearing : false; + const clearMs: number = options && options.clearMs ? options.clearMs : null; + clipboard.writeText(text, type); + if (!clearing) { + this.messagingService.send("copiedToClipboard", { + clipboardValue: text, + clearMs: clearMs, + type: type, + clearing: clearing, + }); + } + } + + readFromClipboard(options?: any): Promise { + const type = options ? options.type : null; + return Promise.resolve(clipboard.readText(type)); + } + + async supportsBiometric(): Promise { + return await this.stateService.getEnableBiometric(); + } + + authenticateBiometric(): Promise { + return new Promise((resolve) => { + const val = ipcRenderer.sendSync("biometric", { + action: "authenticate", + }); + resolve(val); + }); + } + + getDefaultSystemTheme() { + return ipcRenderer.invoke("systemTheme"); + } + + onDefaultSystemThemeChange(callback: (theme: ThemeType.Light | ThemeType.Dark) => unknown) { + ipcRenderer.on("systemThemeUpdated", (event, theme: ThemeType.Light | ThemeType.Dark) => + callback(theme) + ); + } + + async getEffectiveTheme() { + const theme = await this.stateService.getTheme(); + if (theme == null || theme === ThemeType.System) { + return this.getDefaultSystemTheme(); + } else { + return theme; + } + } + + supportsSecureStorage(): boolean { + return true; + } +} diff --git a/jslib/electron/src/services/electronRendererMessaging.service.ts b/jslib/electron/src/services/electronRendererMessaging.service.ts new file mode 100644 index 00000000..89350a65 --- /dev/null +++ b/jslib/electron/src/services/electronRendererMessaging.service.ts @@ -0,0 +1,26 @@ +import { ipcRenderer } from "electron"; + +import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; + +export class ElectronRendererMessagingService implements MessagingService { + constructor(private broadcasterService: BroadcasterService) { + ipcRenderer.on("messagingService", async (event: any, message: any) => { + if (message.command) { + this.sendMessage(message.command, message, false); + } + }); + } + + send(subscriber: string, arg: any = {}) { + this.sendMessage(subscriber, arg, true); + } + + private sendMessage(subscriber: string, arg: any = {}, toMain: boolean) { + const message = Object.assign({}, { command: subscriber }, arg); + this.broadcasterService.send(message); + if (toMain) { + ipcRenderer.send("messagingService", message); + } + } +} diff --git a/jslib/electron/src/services/electronRendererSecureStorage.service.ts b/jslib/electron/src/services/electronRendererSecureStorage.service.ts new file mode 100644 index 00000000..5c3f3c74 --- /dev/null +++ b/jslib/electron/src/services/electronRendererSecureStorage.service.ts @@ -0,0 +1,43 @@ +import { ipcRenderer } from "electron"; + +import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StorageOptions } from "jslib-common/models/domain/storageOptions"; + +export class ElectronRendererSecureStorageService implements StorageService { + async get(key: string, options?: StorageOptions): Promise { + const val = ipcRenderer.sendSync("keytar", { + action: "getPassword", + key: key, + keySuffix: options?.keySuffix ?? "", + }); + return Promise.resolve(val != null ? (JSON.parse(val) as T) : null); + } + + async has(key: string, options?: StorageOptions): Promise { + const val = ipcRenderer.sendSync("keytar", { + action: "hasPassword", + key: key, + keySuffix: options?.keySuffix ?? "", + }); + return Promise.resolve(!!val); + } + + async save(key: string, obj: any, options?: StorageOptions): Promise { + ipcRenderer.sendSync("keytar", { + action: "setPassword", + key: key, + keySuffix: options?.keySuffix ?? "", + value: JSON.stringify(obj), + }); + return Promise.resolve(); + } + + async remove(key: string, options?: StorageOptions): Promise { + ipcRenderer.sendSync("keytar", { + action: "deletePassword", + key: key, + keySuffix: options?.keySuffix ?? "", + }); + return Promise.resolve(); + } +} diff --git a/jslib/electron/src/services/electronRendererStorage.service.ts b/jslib/electron/src/services/electronRendererStorage.service.ts new file mode 100644 index 00000000..69436bfe --- /dev/null +++ b/jslib/electron/src/services/electronRendererStorage.service.ts @@ -0,0 +1,34 @@ +import { ipcRenderer } from "electron"; + +import { StorageService } from "jslib-common/abstractions/storage.service"; + +export class ElectronRendererStorageService implements StorageService { + get(key: string): Promise { + return ipcRenderer.invoke("storageService", { + action: "get", + key: key, + }); + } + + has(key: string): Promise { + return ipcRenderer.invoke("storageService", { + action: "has", + key: key, + }); + } + + save(key: string, obj: any): Promise { + return ipcRenderer.invoke("storageService", { + action: "save", + key: key, + obj: obj, + }); + } + + remove(key: string): Promise { + return ipcRenderer.invoke("storageService", { + action: "remove", + key: key, + }); + } +} diff --git a/jslib/electron/src/services/electronStorage.service.ts b/jslib/electron/src/services/electronStorage.service.ts new file mode 100644 index 00000000..fdabf2a2 --- /dev/null +++ b/jslib/electron/src/services/electronStorage.service.ts @@ -0,0 +1,60 @@ +import * as fs from "fs"; + +import { ipcMain } from "electron"; + +import { StorageService } from "jslib-common/abstractions/storage.service"; +import { NodeUtils } from "jslib-common/misc/nodeUtils"; + +// eslint-disable-next-line +const Store = require("electron-store"); + +export class ElectronStorageService implements StorageService { + private store: any; + + constructor(dir: string, defaults = {}) { + if (!fs.existsSync(dir)) { + NodeUtils.mkdirpSync(dir, "700"); + } + const storeConfig: any = { + defaults: defaults, + name: "data", + }; + this.store = new Store(storeConfig); + + ipcMain.handle("storageService", (event, options) => { + switch (options.action) { + case "get": + return this.get(options.key); + case "has": + return this.has(options.key); + case "save": + return this.save(options.key, options.obj); + case "remove": + return this.remove(options.key); + } + }); + } + + get(key: string): Promise { + const val = this.store.get(key) as T; + return Promise.resolve(val != null ? val : null); + } + + has(key: string): Promise { + const val = this.store.get(key); + return Promise.resolve(val != null); + } + + save(key: string, obj: any): Promise { + if (obj instanceof Set) { + obj = Array.from(obj); + } + this.store.set(key, obj); + return Promise.resolve(); + } + + remove(key: string): Promise { + this.store.delete(key); + return Promise.resolve(); + } +} diff --git a/jslib/electron/src/tray.main.ts b/jslib/electron/src/tray.main.ts new file mode 100644 index 00000000..ebbc5687 --- /dev/null +++ b/jslib/electron/src/tray.main.ts @@ -0,0 +1,186 @@ +import * as path from "path"; + +import { app, BrowserWindow, Menu, MenuItemConstructorOptions, nativeImage, Tray } from "electron"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { StateService } from "jslib-common/abstractions/state.service"; + +import { WindowMain } from "./window.main"; + +export class TrayMain { + contextMenu: Menu; + + private appName: string; + private tray: Tray; + private icon: string | Electron.NativeImage; + private pressedIcon: Electron.NativeImage; + + constructor( + private windowMain: WindowMain, + private i18nService: I18nService, + private stateService: StateService + ) { + if (process.platform === "win32") { + this.icon = path.join(__dirname, "/images/icon.ico"); + } else if (process.platform === "darwin") { + const nImage = nativeImage.createFromPath(path.join(__dirname, "/images/icon-template.png")); + nImage.setTemplateImage(true); + this.icon = nImage; + this.pressedIcon = nativeImage.createFromPath( + path.join(__dirname, "/images/icon-highlight.png") + ); + } else { + this.icon = path.join(__dirname, "/images/icon.png"); + } + } + + async init(appName: string, additionalMenuItems: MenuItemConstructorOptions[] = null) { + this.appName = appName; + + const menuItemOptions: MenuItemConstructorOptions[] = [ + { + label: this.i18nService.t("showHide"), + click: () => this.toggleWindow(), + }, + { type: "separator" }, + { + label: this.i18nService.t("exit"), + click: () => this.closeWindow(), + }, + ]; + + if (additionalMenuItems != null) { + menuItemOptions.splice(1, 0, ...additionalMenuItems); + } + + this.contextMenu = Menu.buildFromTemplate(menuItemOptions); + if (await this.stateService.getEnableTray()) { + this.showTray(); + } + } + + setupWindowListeners(win: BrowserWindow) { + win.on("minimize", async (e: Event) => { + if (await this.stateService.getEnableMinimizeToTray()) { + e.preventDefault(); + this.hideToTray(); + } + }); + + win.on("close", async (e: Event) => { + if (await this.stateService.getEnableCloseToTray()) { + if (!this.windowMain.isQuitting) { + e.preventDefault(); + this.hideToTray(); + } + } + }); + + win.on("show", async () => { + const enableTray = await this.stateService.getEnableTray(); + if (!enableTray) { + setTimeout(() => this.removeTray(false), 100); + } + }); + } + + removeTray(showWindow = true) { + // Due to https://github.com/electron/electron/issues/17622 + // we cannot destroy the tray icon on linux. + if (this.tray != null && process.platform !== "linux") { + this.tray.destroy(); + this.tray = null; + } + + if (showWindow && this.windowMain.win != null && !this.windowMain.win.isVisible()) { + this.windowMain.win.show(); + } + } + + async hideToTray() { + this.showTray(); + if (this.windowMain.win != null) { + this.windowMain.win.hide(); + } + if (this.isDarwin() && !(await this.stateService.getAlwaysShowDock())) { + this.hideDock(); + } + } + + restoreFromTray() { + if (this.windowMain.win == null || !this.windowMain.win.isVisible()) { + this.toggleWindow(); + } + } + + showTray() { + if (this.tray != null) { + return; + } + + this.tray = new Tray(this.icon); + this.tray.setToolTip(this.appName); + this.tray.on("click", () => this.toggleWindow()); + this.tray.on("right-click", () => this.tray.popUpContextMenu(this.contextMenu)); + + if (this.pressedIcon != null) { + this.tray.setPressedImage(this.pressedIcon); + } + if (this.contextMenu != null && !this.isDarwin()) { + this.tray.setContextMenu(this.contextMenu); + } + } + + updateContextMenu() { + if (this.contextMenu != null && this.isLinux()) { + this.tray.setContextMenu(this.contextMenu); + } + } + + private hideDock() { + app.dock.hide(); + } + + private showDock() { + app.dock.show(); + } + + private isDarwin() { + return process.platform === "darwin"; + } + + private isLinux() { + return process.platform === "linux"; + } + + private async toggleWindow() { + if (this.windowMain.win == null) { + if (this.isDarwin()) { + // On MacOS, closing the window via the red button destroys the BrowserWindow instance. + this.windowMain.createWindow().then(() => { + this.windowMain.win.show(); + this.showDock(); + }); + } + return; + } + if (this.windowMain.win.isVisible()) { + this.windowMain.win.hide(); + if (this.isDarwin() && !(await this.stateService.getAlwaysShowDock())) { + this.hideDock(); + } + } else { + this.windowMain.win.show(); + if (this.isDarwin()) { + this.showDock(); + } + } + } + + private closeWindow() { + this.windowMain.isQuitting = true; + if (this.windowMain.win != null) { + this.windowMain.win.close(); + } + } +} diff --git a/jslib/electron/src/updater.main.ts b/jslib/electron/src/updater.main.ts new file mode 100644 index 00000000..881208f8 --- /dev/null +++ b/jslib/electron/src/updater.main.ts @@ -0,0 +1,156 @@ +import { dialog, shell } from "electron"; +import log from "electron-log"; +import { autoUpdater } from "electron-updater"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +import { isAppImage, isDev, isMacAppStore, isWindowsPortable, isWindowsStore } from "./utils"; +import { WindowMain } from "./window.main"; + +const UpdaterCheckInitalDelay = 5 * 1000; // 5 seconds +const UpdaterCheckInterval = 12 * 60 * 60 * 1000; // 12 hours + +export class UpdaterMain { + private doingUpdateCheck = false; + private doingUpdateCheckWithFeedback = false; + private canUpdate = false; + + constructor( + private i18nService: I18nService, + private windowMain: WindowMain, + private gitHubProject: string, + private onCheckingForUpdate: () => void = null, + private onReset: () => void = null, + private onUpdateDownloaded: () => void = null, + private projectName: string + ) { + autoUpdater.logger = log; + + const linuxCanUpdate = process.platform === "linux" && isAppImage(); + const windowsCanUpdate = + process.platform === "win32" && !isWindowsStore() && !isWindowsPortable(); + const macCanUpdate = process.platform === "darwin" && !isMacAppStore(); + this.canUpdate = + process.env.ELECTRON_NO_UPDATER !== "1" && + (linuxCanUpdate || windowsCanUpdate || macCanUpdate); + } + + async init() { + global.setTimeout(async () => await this.checkForUpdate(), UpdaterCheckInitalDelay); + global.setInterval(async () => await this.checkForUpdate(), UpdaterCheckInterval); + + autoUpdater.on("checking-for-update", () => { + if (this.onCheckingForUpdate != null) { + this.onCheckingForUpdate(); + } + this.doingUpdateCheck = true; + }); + + autoUpdater.on("update-available", async () => { + if (this.doingUpdateCheckWithFeedback) { + if (this.windowMain.win == null) { + this.reset(); + return; + } + + const result = await dialog.showMessageBox(this.windowMain.win, { + type: "info", + title: + this.i18nService.t(this.projectName) + " - " + this.i18nService.t("updateAvailable"), + message: this.i18nService.t("updateAvailable"), + detail: this.i18nService.t("updateAvailableDesc"), + buttons: [this.i18nService.t("yes"), this.i18nService.t("no")], + cancelId: 1, + defaultId: 0, + noLink: true, + }); + + if (result.response === 0) { + autoUpdater.downloadUpdate(); + } else { + this.reset(); + } + } + }); + + autoUpdater.on("update-not-available", () => { + if (this.doingUpdateCheckWithFeedback && this.windowMain.win != null) { + dialog.showMessageBox(this.windowMain.win, { + message: this.i18nService.t("noUpdatesAvailable"), + buttons: [this.i18nService.t("ok")], + defaultId: 0, + noLink: true, + }); + } + + this.reset(); + }); + + autoUpdater.on("update-downloaded", async (info) => { + if (this.onUpdateDownloaded != null) { + this.onUpdateDownloaded(); + } + + if (this.windowMain.win == null) { + return; + } + + const result = await dialog.showMessageBox(this.windowMain.win, { + type: "info", + title: this.i18nService.t(this.projectName) + " - " + this.i18nService.t("restartToUpdate"), + message: this.i18nService.t("restartToUpdate"), + detail: this.i18nService.t("restartToUpdateDesc", info.version), + buttons: [this.i18nService.t("restart"), this.i18nService.t("later")], + cancelId: 1, + defaultId: 0, + noLink: true, + }); + + if (result.response === 0) { + // Quit and install have a different window logic, setting `isQuitting` just to be safe. + this.windowMain.isQuitting = true; + autoUpdater.quitAndInstall(true, true); + } + }); + + autoUpdater.on("error", (error) => { + if (this.doingUpdateCheckWithFeedback) { + dialog.showErrorBox( + this.i18nService.t("updateError"), + error == null ? this.i18nService.t("unknown") : (error.stack || error).toString() + ); + } + + this.reset(); + }); + } + + async checkForUpdate(withFeedback = false) { + if (this.doingUpdateCheck || isDev()) { + return; + } + + if (!this.canUpdate) { + if (withFeedback) { + shell.openExternal("https://github.com/bitwarden/" + this.gitHubProject + "/releases"); + } + + return; + } + + this.doingUpdateCheckWithFeedback = withFeedback; + if (withFeedback) { + autoUpdater.autoDownload = false; + } + + await autoUpdater.checkForUpdates(); + } + + private reset() { + if (this.onReset != null) { + this.onReset(); + } + autoUpdater.autoDownload = true; + this.doingUpdateCheck = false; + } +} diff --git a/jslib/electron/src/utils.ts b/jslib/electron/src/utils.ts new file mode 100644 index 00000000..74a40773 --- /dev/null +++ b/jslib/electron/src/utils.ts @@ -0,0 +1,76 @@ +import { ipcRenderer } from "electron"; + +export type RendererMenuItem = { + label?: string; + type?: "normal" | "separator" | "submenu" | "checkbox" | "radio"; + click?: () => any; +}; + +export function invokeMenu(menu: RendererMenuItem[]) { + const menuWithoutClick = menu.map((m) => { + return { label: m.label, type: m.type }; + }); + ipcRenderer.invoke("openContextMenu", { menu: menuWithoutClick }).then((i: number) => { + if (i !== -1) { + menu[i].click(); + } + }); +} + +export function isDev() { + // ref: https://github.com/sindresorhus/electron-is-dev + if ("ELECTRON_IS_DEV" in process.env) { + return parseInt(process.env.ELECTRON_IS_DEV, 10) === 1; + } + return process.defaultApp || /node_modules[\\/]electron[\\/]/.test(process.execPath); +} + +export function isAppImage() { + return process.platform === "linux" && "APPIMAGE" in process.env; +} + +export function isMac() { + return process.platform === "darwin"; +} + +export function isMacAppStore() { + return isMac() && process.mas === true; +} + +export function isWindowsStore() { + const isWindows = process.platform === "win32"; + let windowsStore = process.windowsStore; + if ( + isWindows && + !windowsStore && + process.resourcesPath.indexOf("8bitSolutionsLLC.bitwardendesktop_") > -1 + ) { + windowsStore = true; + } + return isWindows && windowsStore === true; +} + +export function isSnapStore() { + return process.platform === "linux" && process.env.SNAP_USER_DATA != null; +} + +export function isWindowsPortable() { + return process.platform === "win32" && process.env.PORTABLE_EXECUTABLE_DIR != null; +} + +/** + * Sanitize user agent so external resources used by the app can't built data on our users. + */ +export function cleanUserAgent(userAgent: string): string { + const userAgentItem = (startString: string, endString: string) => { + const startIndex = userAgent.indexOf(startString); + return userAgent.substring(startIndex, userAgent.indexOf(endString, startIndex) + 1); + }; + const systemInformation = "(Windows NT 10.0; Win64; x64)"; + + // Set system information, remove bitwarden, and electron information + return userAgent + .replace(userAgentItem("(", ")"), systemInformation) + .replace(userAgentItem("Bitwarden", " "), "") + .replace(userAgentItem("Electron", " "), ""); +} diff --git a/jslib/electron/src/window.main.ts b/jslib/electron/src/window.main.ts new file mode 100644 index 00000000..66d4c780 --- /dev/null +++ b/jslib/electron/src/window.main.ts @@ -0,0 +1,293 @@ +import * as path from "path"; +import * as url from "url"; + +import { app, BrowserWindow, screen } from "electron"; + +import { LogService } from "jslib-common/abstractions/log.service"; +import { StateService } from "jslib-common/abstractions/state.service"; + +import { cleanUserAgent, isDev, isMacAppStore, isSnapStore } from "./utils"; + +const mainWindowSizeKey = "mainWindowSize"; +const WindowEventHandlingDelay = 100; +export class WindowMain { + win: BrowserWindow; + isQuitting = false; + + private windowStateChangeTimer: NodeJS.Timer; + private windowStates: { [key: string]: any } = {}; + private enableAlwaysOnTop = false; + + constructor( + private stateService: StateService, + private logService: LogService, + private hideTitleBar = false, + private defaultWidth = 950, + private defaultHeight = 600, + private argvCallback: (argv: string[]) => void = null, + private createWindowCallback: (win: BrowserWindow) => void + ) {} + + init(): Promise { + return new Promise((resolve, reject) => { + try { + if (!isMacAppStore() && !isSnapStore()) { + const gotTheLock = app.requestSingleInstanceLock(); + if (!gotTheLock) { + app.quit(); + return; + } else { + // eslint-disable-next-line + app.on("second-instance", (event, argv, workingDirectory) => { + // Someone tried to run a second instance, we should focus our window. + if (this.win != null) { + if (this.win.isMinimized() || !this.win.isVisible()) { + this.win.show(); + } + this.win.focus(); + } + if (process.platform === "win32" || process.platform === "linux") { + if (this.argvCallback != null) { + this.argvCallback(argv); + } + } + }); + } + } + + // This method will be called when Electron is shutting + // down the application. + app.on("before-quit", () => { + this.isQuitting = true; + }); + + // This method will be called when Electron has finished + // initialization and is ready to create browser windows. + // Some APIs can only be used after this event occurs. + app.on("ready", async () => { + await this.createWindow(); + resolve(); + if (this.argvCallback != null) { + this.argvCallback(process.argv); + } + }); + + // Quit when all windows are closed. + app.on("window-all-closed", () => { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== "darwin" || this.isQuitting || isMacAppStore()) { + app.quit(); + } + }); + + app.on("activate", async () => { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (this.win === null) { + await this.createWindow(); + } else { + // Show the window when clicking on Dock icon + this.win.show(); + } + }); + } catch (e) { + // Catch Error + // throw e; + reject(e); + } + }); + } + + async createWindow(): Promise { + this.windowStates[mainWindowSizeKey] = await this.getWindowState( + this.defaultWidth, + this.defaultHeight + ); + this.enableAlwaysOnTop = await this.stateService.getEnableAlwaysOnTop(); + + // Create the browser window. + this.win = new BrowserWindow({ + width: this.windowStates[mainWindowSizeKey].width, + height: this.windowStates[mainWindowSizeKey].height, + minWidth: 680, + minHeight: 500, + x: this.windowStates[mainWindowSizeKey].x, + y: this.windowStates[mainWindowSizeKey].y, + title: app.name, + icon: process.platform === "linux" ? path.join(__dirname, "/images/icon.png") : undefined, + titleBarStyle: this.hideTitleBar && process.platform === "darwin" ? "hiddenInset" : undefined, + show: false, + backgroundColor: "#fff", + alwaysOnTop: this.enableAlwaysOnTop, + webPreferences: { + spellcheck: false, + nodeIntegration: true, + backgroundThrottling: false, + contextIsolation: false, + }, + }); + + if (this.windowStates[mainWindowSizeKey].isMaximized) { + this.win.maximize(); + } + + // Show it later since it might need to be maximized. + this.win.show(); + + // and load the index.html of the app. + this.win.loadURL( + url.format({ + protocol: "file:", + pathname: path.join(__dirname, "/index.html"), + slashes: true, + }), + { + userAgent: cleanUserAgent(this.win.webContents.userAgent), + } + ); + + // Open the DevTools. + if (isDev()) { + this.win.webContents.openDevTools(); + } + + // Emitted when the window is closed. + this.win.on("closed", async () => { + await this.updateWindowState(mainWindowSizeKey, this.win); + + // Dereference the window object, usually you would store window + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + this.win = null; + }); + + this.win.on("close", async () => { + await this.updateWindowState(mainWindowSizeKey, this.win); + }); + + this.win.on("maximize", async () => { + await this.updateWindowState(mainWindowSizeKey, this.win); + }); + + this.win.on("unmaximize", async () => { + await this.updateWindowState(mainWindowSizeKey, this.win); + }); + + this.win.on("resize", () => { + this.windowStateChangeHandler(mainWindowSizeKey, this.win); + }); + + this.win.on("move", () => { + this.windowStateChangeHandler(mainWindowSizeKey, this.win); + }); + this.win.on("focus", () => { + this.win.webContents.send("messagingService", { + command: "windowIsFocused", + windowIsFocused: true, + }); + }); + + if (this.createWindowCallback) { + this.createWindowCallback(this.win); + } + } + + async toggleAlwaysOnTop() { + this.enableAlwaysOnTop = !this.win.isAlwaysOnTop(); + this.win.setAlwaysOnTop(this.enableAlwaysOnTop); + await this.stateService.setEnableAlwaysOnTop(this.enableAlwaysOnTop); + } + + private windowStateChangeHandler(configKey: string, win: BrowserWindow) { + global.clearTimeout(this.windowStateChangeTimer); + this.windowStateChangeTimer = global.setTimeout(async () => { + await this.updateWindowState(configKey, win); + }, WindowEventHandlingDelay); + } + + private async updateWindowState(configKey: string, win: BrowserWindow) { + if (win == null) { + return; + } + + try { + const bounds = win.getBounds(); + + if (this.windowStates[configKey] == null) { + this.windowStates[configKey] = await this.stateService.getWindow(); + if (this.windowStates[configKey] == null) { + this.windowStates[configKey] = {}; + } + } + + this.windowStates[configKey].isMaximized = win.isMaximized(); + this.windowStates[configKey].displayBounds = screen.getDisplayMatching(bounds).bounds; + + if (!win.isMaximized() && !win.isMinimized() && !win.isFullScreen()) { + this.windowStates[configKey].x = bounds.x; + this.windowStates[configKey].y = bounds.y; + this.windowStates[configKey].width = bounds.width; + this.windowStates[configKey].height = bounds.height; + } + + await this.stateService.setWindow(this.windowStates[configKey]); + } catch (e) { + this.logService.error(e); + } + } + + private async getWindowState(defaultWidth: number, defaultHeight: number) { + const state = await this.stateService.getWindow(); + + const isValid = state != null && (this.stateHasBounds(state) || state.isMaximized); + let displayBounds: Electron.Rectangle = null; + if (!isValid) { + state.width = defaultWidth; + state.height = defaultHeight; + + displayBounds = screen.getPrimaryDisplay().bounds; + } else if (this.stateHasBounds(state) && state.displayBounds) { + // Check if the display where the window was last open is still available + displayBounds = screen.getDisplayMatching(state.displayBounds).bounds; + + if ( + displayBounds.width !== state.displayBounds.width || + displayBounds.height !== state.displayBounds.height || + displayBounds.x !== state.displayBounds.x || + displayBounds.y !== state.displayBounds.y + ) { + state.x = undefined; + state.y = undefined; + displayBounds = screen.getPrimaryDisplay().bounds; + } + } + + if (displayBounds != null) { + if (state.width > displayBounds.width && state.height > displayBounds.height) { + state.isMaximized = true; + } + + if (state.width > displayBounds.width) { + state.width = displayBounds.width - 10; + } + if (state.height > displayBounds.height) { + state.height = displayBounds.height - 10; + } + } + + return state; + } + + private stateHasBounds(state: any): boolean { + return ( + state != null && + Number.isInteger(state.x) && + Number.isInteger(state.y) && + Number.isInteger(state.width) && + state.width > 0 && + Number.isInteger(state.height) && + state.height > 0 + ); + } +} diff --git a/jslib/electron/tsconfig.json b/jslib/electron/tsconfig.json new file mode 100644 index 00000000..d5501ab3 --- /dev/null +++ b/jslib/electron/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../shared/tsconfig", + "compilerOptions": { + "paths": { + "jslib-common/*": ["../common/src/*"], + "jslib-electron/*": ["./src/*"] + } + }, + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/electron/tsconfig.spec.json b/jslib/electron/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/electron/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/jslib/jest.config.js b/jslib/jest.config.js new file mode 100644 index 00000000..fabe9db7 --- /dev/null +++ b/jslib/jest.config.js @@ -0,0 +1,18 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), + projects: [ + "/angular/jest.config.js", + "/common/jest.config.js", + "/electron/jest.config.js", + "/node/jest.config.js", + ], +}; diff --git a/jslib/node/jest.config.js b/jslib/node/jest.config.js new file mode 100644 index 00000000..f1bfb7a9 --- /dev/null +++ b/jslib/node/jest.config.js @@ -0,0 +1,15 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + preset: "ts-jest", + testMatch: ["**/+(*.)+(spec).+(ts)"], + setupFilesAfterEnv: ["/spec/test.ts"], + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/jslib/node/package-lock.json b/jslib/node/package-lock.json new file mode 100644 index 00000000..ab1b64a3 --- /dev/null +++ b/jslib/node/package-lock.json @@ -0,0 +1,1183 @@ +{ + "name": "@bitwarden/jslib-node", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib-node", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "chalk": "^4.1.1", + "commander": "7.2.0", + "form-data": "4.0.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.0.0", + "lowdb": "1.0.0", + "node-fetch": "^2.6.1" + }, + "devDependencies": { + "@types/inquirer": "^7.3.1", + "@types/lowdb": "^1.0.10", + "@types/node": "^16.11.12", + "@types/node-fetch": "^2.5.10", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "../common": { + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^0.10.0", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^0.9.7", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@bitwarden/jslib-common": { + "resolved": "../common", + "link": true + }, + "node_modules/@types/inquirer": { + "version": "7.3.3", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "node_modules/@types/lodash": { + "version": "4.14.175", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==", + "dev": true + }, + "node_modules/@types/lowdb": { + "version": "1.0.11", + "integrity": "sha512-h99VMxvTuz+VsXUVCCJo4dsps4vbkXwvU71TpmxDoiBU24bJ0VBygIHgmMm+UPoQIFihmV6euRik4z8J7XDJWg==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/node": { + "version": "16.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", + "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.5.12", + "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/through": { + "version": "0.0.30", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.2", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.0", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inquirer": { + "version": "8.0.0", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lowdb": { + "version": "1.0.0", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "dependencies": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.50.0", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.33", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "dependencies": { + "mime-db": "1.50.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/node-fetch": { + "version": "2.6.5", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/signal-exit": { + "version": "3.0.5", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" + }, + "node_modules/steno": { + "version": "0.4.4", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "dependencies": { + "graceful-fs": "^4.1.3" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/tmp": { + "version": "0.0.33", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/tslib": { + "version": "1.14.1", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + }, + "dependencies": { + "@bitwarden/jslib-common": { + "version": "file:../common", + "requires": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^0.9.7", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^0.10.0", + "papaparse": "^5.3.0", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zxcvbn": "^4.4.2" + } + }, + "@types/inquirer": { + "version": "7.3.3", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dev": true, + "requires": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "@types/lodash": { + "version": "4.14.175", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==", + "dev": true + }, + "@types/lowdb": { + "version": "1.0.11", + "integrity": "sha512-h99VMxvTuz+VsXUVCCJo4dsps4vbkXwvU71TpmxDoiBU24bJ0VBygIHgmMm+UPoQIFihmV6euRik4z8J7XDJWg==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/node": { + "version": "16.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", + "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", + "dev": true + }, + "@types/node-fetch": { + "version": "2.5.12", + "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/through": { + "version": "0.0.30", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "agent-base": { + "version": "6.0.2", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "asynckit": { + "version": "0.4.0", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "balanced-match": { + "version": "1.0.2", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "cli-cursor": { + "version": "3.1.0", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "color-convert": { + "version": "2.0.1", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "combined-stream": { + "version": "1.0.8", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "7.2.0", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "concat-map": { + "version": "0.0.1", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "debug": { + "version": "4.3.2", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "emoji-regex": { + "version": "8.0.0", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "external-editor": { + "version": "3.1.0", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "figures": { + "version": "3.2.0", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "form-data": { + "version": "4.0.0", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "has-flag": { + "version": "4.0.0", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "https-proxy-agent": { + "version": "5.0.0", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "iconv-lite": { + "version": "0.4.24", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflight": { + "version": "1.0.6", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "8.0.0", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-promise": { + "version": "2.2.2", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "lodash": { + "version": "4.17.21", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lowdb": { + "version": "1.0.0", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "requires": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + } + }, + "mime-db": { + "version": "1.50.0", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==" + }, + "mime-types": { + "version": "2.1.33", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "requires": { + "mime-db": "1.50.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.8", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node-fetch": { + "version": "2.6.5", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "once": { + "version": "1.4.0", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "path-is-absolute": { + "version": "1.0.1", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "restore-cursor": { + "version": "3.1.0", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "3.0.2", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "rxjs": { + "version": "6.6.7", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "signal-exit": { + "version": "3.0.5", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" + }, + "steno": { + "version": "0.4.4", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "requires": { + "graceful-fs": "^4.1.3" + } + }, + "string-width": { + "version": "4.2.3", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "through": { + "version": "2.3.8", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tmp": { + "version": "0.0.33", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tr46": { + "version": "0.0.3", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "tslib": { + "version": "1.14.1", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "type-fest": { + "version": "0.21.3", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/jslib/node/package.json b/jslib/node/package.json new file mode 100644 index 00000000..b5c6c225 --- /dev/null +++ b/jslib/node/package.json @@ -0,0 +1,38 @@ +{ + "name": "@bitwarden/jslib-node", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + }, + "devDependencies": { + "@types/inquirer": "^7.3.1", + "@types/lowdb": "^1.0.10", + "@types/node": "^16.11.12", + "@types/node-fetch": "^2.5.10", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "chalk": "^4.1.1", + "commander": "7.2.0", + "form-data": "4.0.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.0.0", + "lowdb": "1.0.0", + "node-fetch": "^2.6.1" + } +} diff --git a/jslib/node/spec/cli/consoleLog.service.spec.ts b/jslib/node/spec/cli/consoleLog.service.spec.ts new file mode 100644 index 00000000..3c76d5a6 --- /dev/null +++ b/jslib/node/spec/cli/consoleLog.service.spec.ts @@ -0,0 +1,45 @@ +import { + interceptConsole, + restoreConsole, +} from "jslib-common/../spec/services/consolelog.service.spec"; + +import { ConsoleLogService } from "jslib-node/cli/services/consoleLog.service"; + +let caughtMessage: any = {}; + +describe("CLI Console log service", () => { + let logService: ConsoleLogService; + beforeEach(() => { + caughtMessage = {}; + interceptConsole(caughtMessage); + logService = new ConsoleLogService(true); + }); + + afterAll(() => { + restoreConsole(); + }); + + it("should redirect all console to error if BW_RESPONSE env is true", () => { + process.env.BW_RESPONSE = "true"; + + logService.debug("this is a debug message"); + expect(caughtMessage).toMatchObject({ + error: { 0: "this is a debug message" }, + }); + }); + + it("should not redirect console to error if BW_RESPONSE != true", () => { + process.env.BW_RESPONSE = "false"; + + logService.debug("debug"); + logService.info("info"); + logService.warning("warning"); + logService.error("error"); + + expect(caughtMessage).toMatchObject({ + log: { 0: "info" }, + warn: { 0: "warning" }, + error: { 0: "error" }, + }); + }); +}); diff --git a/jslib/node/spec/services/nodeCryptoFunction.service.spec.ts b/jslib/node/spec/services/nodeCryptoFunction.service.spec.ts new file mode 100644 index 00000000..79572fcc --- /dev/null +++ b/jslib/node/spec/services/nodeCryptoFunction.service.spec.ts @@ -0,0 +1,518 @@ +import { Utils } from "jslib-common/misc/utils"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { NodeCryptoFunctionService } from "jslib-node/services/nodeCryptoFunction.service"; + +const RsaPublicKey = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" + + "4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP" + + "RQ1H7iNHVZz3K8Db9GCSKPeC8MbW6gVCzb15esCe1gGzg6wkMuWYDFYPoh/oBqcIqrGah7firqB1nDedzEjw32heP2DAffVN" + + "084iTDjiWrJNUxBJ2pDD5Z9dT3MzQ2s09ew1yMWK2z37rT3YerC7OgEDmo3WYo3xL3qYJznu3EO2nmrYjiRa40wKSjxsTlUc" + + "xDF+F0uMW8oR9EMUHgepdepfAtLsSAQIDAQAB"; +const RsaPrivateKey = + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXRVrCX+2hfOQS8Hz" + + "YUS2oc/jGVTZpv+/Ryuoh9d8ihYX9dd0cYh2tl6KWdFc88lPUH11Oxqy20Rk2e5r/RF6T9yM0Me3NPnaKt+hlhLtfoc0h86L" + + "nhD56A9FDUfuI0dVnPcrwNv0YJIo94LwxtbqBULNvXl6wJ7WAbODrCQy5ZgMVg+iH+gGpwiqsZqHt+KuoHWcN53MSPDfaF4/" + + "YMB99U3TziJMOOJask1TEEnakMPln11PczNDazT17DXIxYrbPfutPdh6sLs6AQOajdZijfEvepgnOe7cQ7aeatiOJFrjTApK" + + "PGxOVRzEMX4XS4xbyhH0QxQeB6l16l8C0uxIBAgMBAAECggEASaWfeVDA3cVzOPFSpvJm20OTE+R6uGOU+7vh36TX/POq92q" + + "Buwbd0h0oMD32FxsXywd2IxtBDUSiFM9699qufTVuM0Q3tZw6lHDTOVG08+tPdr8qSbMtw7PGFxN79fHLBxejjO4IrM9lapj" + + "WpxEF+11x7r+wM+0xRZQ8sNFYG46aPfIaty4BGbL0I2DQ2y8I57iBCAy69eht59NLMm27fRWGJIWCuBIjlpfzET1j2HLXUIh" + + "5bTBNzqaN039WH49HczGE3mQKVEJZc/efk3HaVd0a1Sjzyn0QY+N1jtZN3jTRbuDWA1AknkX1LX/0tUhuS3/7C3ejHxjw4Dk" + + "1ZLo5/QKBgQDIWvqFn0+IKRSu6Ua2hDsufIHHUNLelbfLUMmFthxabcUn4zlvIscJO00Tq/ezopSRRvbGiqnxjv/mYxucvOU" + + "BeZtlus0Q9RTACBtw9TGoNTmQbEunJ2FOSlqbQxkBBAjgGEppRPt30iGj/VjAhCATq2MYOa/X4dVR51BqQAFIEwKBgQDBSIf" + + "TFKC/hDk6FKZlgwvupWYJyU9RkyfstPErZFmzoKhPkQ3YORo2oeAYmVUbS9I2iIYpYpYQJHX8jMuCbCz4ONxTCuSIXYQYUcU" + + "q4PglCKp31xBAE6TN8SvhfME9/MvuDssnQinAHuF0GDAhF646T3LLS1not6Vszv7brwSoGwKBgQC88v/8cGfi80ssQZeMnVv" + + "q1UTXIeQcQnoY5lGHJl3K8mbS3TnXE6c9j417Fdz+rj8KWzBzwWXQB5pSPflWcdZO886Xu/mVGmy9RWgLuVFhXwCwsVEPjNX" + + "5ramRb0/vY0yzenUCninBsIxFSbIfrPtLUYCc4hpxr+sr2Mg/y6jpvQKBgBezMRRs3xkcuXepuI2R+BCXL1/b02IJTUf1F+1" + + "eLLGd7YV0H+J3fgNc7gGWK51hOrF9JBZHBGeOUPlaukmPwiPdtQZpu4QNE3l37VlIpKTF30E6mb+BqR+nht3rUjarnMXgAoE" + + "Z18y6/KIjpSMpqC92Nnk/EBM9EYe6Cf4eA9ApAoGAeqEUg46UTlJySkBKURGpIs3v1kkf5I0X8DnOhwb+HPxNaiEdmO7ckm8" + + "+tPVgppLcG0+tMdLjigFQiDUQk2y3WjyxP5ZvXu7U96jaJRI8PFMoE06WeVYcdIzrID2HvqH+w0UQJFrLJ/0Mn4stFAEzXKZ" + + "BokBGnjFnTnKcs7nv/O8="; + +const Sha1Mac = "4d4c223f95dc577b665ec4ccbcb680b80a397038"; +const Sha256Mac = "6be3caa84922e12aaaaa2f16c40d44433bb081ef323db584eb616333ab4e874f"; +const Sha512Mac = + "21910e341fa12106ca35758a2285374509326c9fbe0bd64e7b99c898f841dc948c58ce66d3504d8883c" + + "5ea7817a0b7c5d4d9b00364ccd214669131fc17fe4aca"; + +describe("NodeCrypto Function Service", () => { + describe("pbkdf2", () => { + const regular256Key = "pj9prw/OHPleXI6bRdmlaD+saJS4awrMiQsQiDjeu2I="; + const utf8256Key = "yqvoFXgMRmHR3QPYr5pyR4uVuoHkltv9aHUP63p8n7I="; + const unicode256Key = "ZdeOata6xoRpB4DLp8zHhXz5kLmkWtX5pd+TdRH8w8w="; + + const regular512Key = + "liTi/Ke8LPU1Qv+Vl7NGEVt/XMbsBVJ2kQxtVG/Z1/JFHFKQW3ZkI81qVlwTiCpb+cFXzs+57" + + "eyhhx5wfKo5Cg=="; + const utf8512Key = + "df0KdvIBeCzD/kyXptwQohaqUa4e7IyFUyhFQjXCANu5T+scq55hCcE4dG4T/MhAk2exw8j7ixRN" + + "zXANiVZpnw=="; + const unicode512Key = + "FE+AnUJaxv8jh+zUDtZz4mjjcYk0/PZDZm+SLJe3XtxtnpdqqpblX6JjuMZt/dYYNMOrb2+mD" + + "L3FiQDTROh1lg=="; + + testPbkdf2("sha256", regular256Key, utf8256Key, unicode256Key); + testPbkdf2("sha512", regular512Key, utf8512Key, unicode512Key); + }); + + describe("hkdf", () => { + const regular256Key = "qBUmEYtwTwwGPuw/z6bs/qYXXYNUlocFlyAuuANI8Pw="; + const utf8256Key = "6DfJwW1R3txgiZKkIFTvVAb7qVlG7lKcmJGJoxR2GBU="; + const unicode256Key = "gejGI82xthA+nKtKmIh82kjw+ttHr+ODsUoGdu5sf0A="; + + const regular512Key = "xe5cIG6ZfwGmb1FvsOedM0XKOm21myZkjL/eDeKIqqM="; + const utf8512Key = "XQMVBnxVEhlvjSFDQc77j5GDE9aorvbS0vKnjhRg0LY="; + const unicode512Key = "148GImrTbrjaGAe/iWEpclINM8Ehhko+9lB14+52lqc="; + + testHkdf("sha256", regular256Key, utf8256Key, unicode256Key); + testHkdf("sha512", regular512Key, utf8512Key, unicode512Key); + }); + + describe("hkdfExpand", () => { + const prk16Byte = "criAmKtfzxanbgea5/kelQ=="; + const prk32Byte = "F5h4KdYQnIVH4rKH0P9CZb1GrR4n16/sJrS0PsQEn0Y="; + const prk64Byte = + "ssBK0mRG17VHdtsgt8yo4v25CRNpauH+0r2fwY/E9rLyaFBAOMbIeTry+" + + "gUJ28p8y+hFh3EI9pcrEWaNvFYonQ=="; + + testHkdfExpand("sha256", prk32Byte, 32, "BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD8="); + testHkdfExpand( + "sha256", + prk32Byte, + 64, + "BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD9BV+" + + "/queOZenPNkDhmlVyL2WZ3OSU5+7ISNF5NhNfvZA==" + ); + testHkdfExpand("sha512", prk64Byte, 32, "uLWbMWodSBms5uGJ5WTRTesyW+MD7nlpCZvagvIRXlk="); + testHkdfExpand( + "sha512", + prk64Byte, + 64, + "uLWbMWodSBms5uGJ5WTRTesyW+MD7nlpCZvagvIRXlkY5Pv0sB+" + + "MqvaopmkC6sD/j89zDwTV9Ib2fpucUydO8w==" + ); + + it("should fail with prk too small", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const f = cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(prk16Byte), + "info", + 32, + "sha256" + ); + await expect(f).rejects.toEqual(new Error("prk is too small.")); + }); + + it("should fail with outputByteSize is too large", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const f = cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(prk32Byte), + "info", + 8161, + "sha256" + ); + await expect(f).rejects.toEqual(new Error("outputByteSize is too large.")); + }); + }); + + describe("hash", () => { + const regular1Hash = "2a241604fb921fad12bf877282457268e1dccb70"; + const utf81Hash = "85672798dc5831e96d6c48655d3d39365a9c88b6"; + const unicode1Hash = "39c975935054a3efc805a9709b60763a823a6ad4"; + + const regular256Hash = "2b8e96031d352a8655d733d7a930b5ffbea69dc25cf65c7bca7dd946278908b2"; + const utf8256Hash = "25fe8440f5b01ed113b0a0e38e721b126d2f3f77a67518c4a04fcde4e33eeb9d"; + const unicode256Hash = "adc1c0c2afd6e92cefdf703f9b6eb2c38e0d6d1a040c83f8505c561fea58852e"; + + const regular512Hash = + "c15cf11d43bde333647e3f559ec4193bb2edeaa0e8b902772f514cdf3f785a3f49a6e02a4b87b3" + + "b47523271ad45b7e0aebb5cdcc1bc54815d256eb5dcb80da9d"; + const utf8512Hash = + "035c31a877a291af09ed2d3a1a293e69c3e079ea2cecc00211f35e6bce10474ca3ad6e30b59e26118" + + "37463f20969c5bc95282965a051a88f8cdf2e166549fcdd"; + const unicode512Hash = + "2b16a5561af8ad6fe414cc103fc8036492e1fc6d9aabe1b655497054f760fe0e34c5d100ac773d" + + "9f3030438284f22dbfa20cb2e9b019f2c98dfe38ce1ef41bae"; + + const regularMd5 = "5eceffa53a5fd58c44134211e2c5f522"; + const utf8Md5 = "3abc9433c09551b939c80aa0aa3174e1"; + const unicodeMd5 = "85ae134072c8d81257933f7045ba17ca"; + + testHash("sha1", regular1Hash, utf81Hash, unicode1Hash); + testHash("sha256", regular256Hash, utf8256Hash, unicode256Hash); + testHash("sha512", regular512Hash, utf8512Hash, unicode512Hash); + testHash("md5", regularMd5, utf8Md5, unicodeMd5); + }); + + describe("hmac", () => { + testHmac("sha1", Sha1Mac); + testHmac("sha256", Sha256Mac); + testHmac("sha512", Sha512Mac); + }); + + describe("compare", () => { + testCompare(false); + }); + + describe("hmacFast", () => { + testHmac("sha1", Sha1Mac, true); + testHmac("sha256", Sha256Mac, true); + testHmac("sha512", Sha512Mac, true); + }); + + describe("compareFast", () => { + testCompare(true); + }); + + describe("aesEncrypt", () => { + it("should successfully encrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = Utils.fromUtf8ToArray("EncryptMe!"); + const encValue = await nodeCryptoFunctionService.aesEncrypt( + data.buffer, + iv.buffer, + key.buffer + ); + expect(Utils.fromBufferToB64(encValue)).toBe("ByUF8vhyX4ddU9gcooznwA=="); + }); + + it("should successfully encrypt and then decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await nodeCryptoFunctionService.aesEncrypt( + data.buffer, + iv.buffer, + key.buffer + ); + const decValue = await nodeCryptoFunctionService.aesDecrypt(encValue, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe("aesDecryptFast", () => { + it("should successfully decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer); + const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer); + const data = "ByUF8vhyX4ddU9gcooznwA=="; + const params = nodeCryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey); + const decValue = await nodeCryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe("EncryptMe!"); + }); + }); + + describe("aesDecrypt", () => { + it("should successfully decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = Utils.fromB64ToArray("ByUF8vhyX4ddU9gcooznwA=="); + const decValue = await nodeCryptoFunctionService.aesDecrypt( + data.buffer, + iv.buffer, + key.buffer + ); + expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); + }); + }); + + describe("rsaEncrypt", () => { + it("should successfully encrypt and then decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const pubKey = Utils.fromB64ToArray(RsaPublicKey); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await nodeCryptoFunctionService.rsaEncrypt( + data.buffer, + pubKey.buffer, + "sha1" + ); + const decValue = await nodeCryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, "sha1"); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe("rsaDecrypt", () => { + it("should successfully decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const data = Utils.fromB64ToArray( + "A1/p8BQzN9UrbdYxUY2Va5+kPLyfZXF9JsZrjeEXcaclsnHurdxVAJcnbEqYMP3UXV" + + "4YAS/mpf+Rxe6/X0WS1boQdA0MAHSgx95hIlAraZYpiMLLiJRKeo2u8YivCdTM9V5vuAEJwf9Tof/qFsFci3sApdbATkorCT" + + "zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D" + + "/5jcercUtK2o+XrzNrL4UQ7yLZcFz6Bfwb/j6ICYvqd/YJwXNE6dwlL57OfwJyCdw2rRYf0/qI00t9u8Iitw==" + ); + const decValue = await nodeCryptoFunctionService.rsaDecrypt( + data.buffer, + privKey.buffer, + "sha1" + ); + expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); + }); + }); + + describe("rsaExtractPublicKey", () => { + it("should successfully extract key", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const publicKey = await nodeCryptoFunctionService.rsaExtractPublicKey(privKey.buffer); + expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey); + }); + }); + + describe("rsaGenerateKeyPair", () => { + testRsaGenerateKeyPair(1024); + testRsaGenerateKeyPair(2048); + + // Generating 4096 bit keys is really slow with Forge lib. + // Maybe move to something else if we ever want to generate keys of this size. + // testRsaGenerateKeyPair(4096); + }); + + describe("randomBytes", () => { + it("should make a value of the correct length", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const randomData = await nodeCryptoFunctionService.randomBytes(16); + expect(randomData.byteLength).toBe(16); + }); + + it("should not make the same value twice", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const randomData = await nodeCryptoFunctionService.randomBytes(16); + const randomData2 = await nodeCryptoFunctionService.randomBytes(16); + expect( + randomData.byteLength === randomData2.byteLength && randomData !== randomData2 + ).toBeTruthy(); + }); + }); +}); + +function testPbkdf2( + algorithm: "sha256" | "sha512", + regularKey: string, + utf8Key: string, + unicodeKey: string +) { + const regularEmail = "user@example.com"; + const utf8Email = "üser@example.com"; + + const regularPassword = "password"; + const utf8Password = "pǻssword"; + const unicodePassword = "😀password🙏"; + + it("should create valid " + algorithm + " key from regular input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(regularPassword, regularEmail, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); + + it("should create valid " + algorithm + " key from utf8 input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(utf8Password, utf8Email, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(utf8Key); + }); + + it("should create valid " + algorithm + " key from unicode input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(unicodePassword, regularEmail, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(unicodeKey); + }); + + it("should create valid " + algorithm + " key from array buffer input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2( + Utils.fromUtf8ToArray(regularPassword).buffer, + Utils.fromUtf8ToArray(regularEmail).buffer, + algorithm, + 5000 + ); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); +} + +function testHkdf( + algorithm: "sha256" | "sha512", + regularKey: string, + utf8Key: string, + unicodeKey: string +) { + const ikm = Utils.fromB64ToArray("criAmKtfzxanbgea5/kelQ=="); + + const regularSalt = "salt"; + const utf8Salt = "üser_salt"; + const unicodeSalt = "😀salt🙏"; + + const regularInfo = "info"; + const utf8Info = "üser_info"; + const unicodeInfo = "😀info🙏"; + + it("should create valid " + algorithm + " key from regular input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, regularSalt, regularInfo, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); + + it("should create valid " + algorithm + " key from utf8 input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, utf8Salt, utf8Info, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(utf8Key); + }); + + it("should create valid " + algorithm + " key from unicode input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, unicodeSalt, unicodeInfo, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(unicodeKey); + }); + + it("should create valid " + algorithm + " key from array buffer input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf( + ikm, + Utils.fromUtf8ToArray(regularSalt).buffer, + Utils.fromUtf8ToArray(regularInfo).buffer, + 32, + algorithm + ); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); +} + +function testHkdfExpand( + algorithm: "sha256" | "sha512", + b64prk: string, + outputByteSize: number, + b64ExpectedOkm: string +) { + const info = "info"; + + it("should create valid " + algorithm + " " + outputByteSize + " byte okm", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const okm = await cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(b64prk), + info, + outputByteSize, + algorithm + ); + expect(Utils.fromBufferToB64(okm)).toBe(b64ExpectedOkm); + }); +} + +function testHash( + algorithm: "sha1" | "sha256" | "sha512" | "md5", + regularHash: string, + utf8Hash: string, + unicodeHash: string +) { + const regularValue = "HashMe!!"; + const utf8Value = "HǻshMe!!"; + const unicodeValue = "😀HashMe!!!🙏"; + + it("should create valid " + algorithm + " hash from regular input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(regularValue, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(regularHash); + }); + + it("should create valid " + algorithm + " hash from utf8 input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(utf8Value, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(utf8Hash); + }); + + it("should create valid " + algorithm + " hash from unicode input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(unicodeValue, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(unicodeHash); + }); + + it("should create valid " + algorithm + " hash from array buffer input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const hash = await cryptoFunctionService.hash( + Utils.fromUtf8ToArray(regularValue).buffer, + algorithm + ); + expect(Utils.fromBufferToHex(hash)).toBe(regularHash); + }); +} + +function testHmac(algorithm: "sha1" | "sha256" | "sha512", mac: string, fast = false) { + it("should create valid " + algorithm + " hmac", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const value = Utils.fromUtf8ToArray("SignMe!!").buffer; + const key = Utils.fromUtf8ToArray("secretkey").buffer; + let computedMac: ArrayBuffer = null; + if (fast) { + computedMac = await cryptoFunctionService.hmacFast(value, key, algorithm); + } else { + computedMac = await cryptoFunctionService.hmac(value, key, algorithm); + } + expect(Utils.fromBufferToHex(computedMac)).toBe(mac); + }); +} + +function testCompare(fast = false) { + it("should successfully compare two of the same values", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const equal = fast + ? await cryptoFunctionService.compareFast(a.buffer, a.buffer) + : await cryptoFunctionService.compare(a.buffer, a.buffer); + expect(equal).toBe(true); + }); + + it("should successfully compare two different values of the same length", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const b = new Uint8Array(2); + b[0] = 3; + b[1] = 4; + const equal = fast + ? await cryptoFunctionService.compareFast(a.buffer, b.buffer) + : await cryptoFunctionService.compare(a.buffer, b.buffer); + expect(equal).toBe(false); + }); + + it("should successfully compare two different values of different lengths", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const b = new Uint8Array(2); + b[0] = 3; + const equal = fast + ? await cryptoFunctionService.compareFast(a.buffer, b.buffer) + : await cryptoFunctionService.compare(a.buffer, b.buffer); + expect(equal).toBe(false); + }); +} + +function testRsaGenerateKeyPair(length: 1024 | 2048 | 4096) { + it( + "should successfully generate a " + length + " bit key pair", + async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const keyPair = await cryptoFunctionService.rsaGenerateKeyPair(length); + expect(keyPair[0] == null || keyPair[1] == null).toBe(false); + const publicKey = await cryptoFunctionService.rsaExtractPublicKey(keyPair[1]); + expect(Utils.fromBufferToB64(keyPair[0])).toBe(Utils.fromBufferToB64(publicKey)); + }, + 30000 + ); +} + +function makeStaticByteArray(length: number) { + const arr = new Uint8Array(length); + for (let i = 0; i < length; i++) { + arr[i] = i; + } + return arr; +} diff --git a/jslib/node/spec/test.ts b/jslib/node/spec/test.ts new file mode 100644 index 00000000..e69de29b diff --git a/jslib/node/src/cli/baseProgram.ts b/jslib/node/src/cli/baseProgram.ts new file mode 100644 index 00000000..4a2ec904 --- /dev/null +++ b/jslib/node/src/cli/baseProgram.ts @@ -0,0 +1,113 @@ +import * as chalk from "chalk"; + +import { StateService } from "jslib-common/abstractions/state.service"; + +import { Response } from "./models/response"; +import { ListResponse } from "./models/response/listResponse"; +import { MessageResponse } from "./models/response/messageResponse"; +import { StringResponse } from "./models/response/stringResponse"; + +export abstract class BaseProgram { + constructor( + protected stateService: StateService, + private writeLn: (s: string, finalLine: boolean, error: boolean) => void + ) {} + + protected processResponse( + response: Response, + exitImmediately = false, + dataProcessor: () => string = null + ) { + if (!response.success) { + if (process.env.BW_QUIET !== "true") { + if (process.env.BW_RESPONSE === "true") { + this.writeLn(this.getJson(response), true, false); + } else { + this.writeLn(chalk.redBright(response.message), true, true); + } + } + const exitCode = process.env.BW_CLEANEXIT ? 0 : 1; + if (exitImmediately) { + process.exit(exitCode); + } else { + process.exitCode = exitCode; + } + return; + } + + if (process.env.BW_RESPONSE === "true") { + this.writeLn(this.getJson(response), true, false); + } else if (response.data != null) { + let out: string = dataProcessor != null ? dataProcessor() : null; + if (out == null) { + if (response.data.object === "string") { + const data = (response.data as StringResponse).data; + if (data != null) { + out = data; + } + } else if (response.data.object === "list") { + out = this.getJson((response.data as ListResponse).data); + } else if (response.data.object === "message") { + out = this.getMessage(response); + } else { + out = this.getJson(response.data); + } + } + + if (out != null && process.env.BW_QUIET !== "true") { + this.writeLn(out, true, false); + } + } + if (exitImmediately) { + process.exit(0); + } else { + process.exitCode = 0; + } + } + + protected getJson(obj: any): string { + if (process.env.BW_PRETTY === "true") { + return JSON.stringify(obj, null, " "); + } else { + return JSON.stringify(obj); + } + } + + protected getMessage(response: Response): string { + const message = response.data as MessageResponse; + if (process.env.BW_RAW === "true") { + return message.raw; + } + + let out = ""; + if (message.title != null) { + if (message.noColor) { + out = message.title; + } else { + out = chalk.greenBright(message.title); + } + } + if (message.message != null) { + if (message.title != null) { + out += "\n"; + } + out += message.message; + } + return out.trim() === "" ? null : out; + } + + protected async exitIfAuthed() { + const authed = await this.stateService.getIsAuthenticated(); + if (authed) { + const email = await this.stateService.getEmail(); + this.processResponse(Response.error("You are already logged in as " + email + "."), true); + } + } + + protected async exitIfNotAuthed() { + const authed = await this.stateService.getIsAuthenticated(); + if (!authed) { + this.processResponse(Response.error("You are not logged in."), true); + } + } +} diff --git a/jslib/node/src/cli/commands/login.command.ts b/jslib/node/src/cli/commands/login.command.ts new file mode 100644 index 00000000..ee6f8228 --- /dev/null +++ b/jslib/node/src/cli/commands/login.command.ts @@ -0,0 +1,624 @@ +import * as http from "http"; + +import * as program from "commander"; +import * as inquirer from "inquirer"; +import Separator from "inquirer/lib/objects/separator"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AuthService } from "jslib-common/abstractions/auth.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { PolicyService } from "jslib-common/abstractions/policy.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType"; +import { NodeUtils } from "jslib-common/misc/nodeUtils"; +import { Utils } from "jslib-common/misc/utils"; +import { AuthResult } from "jslib-common/models/domain/authResult"; +import { + ApiLogInCredentials, + PasswordLogInCredentials, + SsoLogInCredentials, +} from "jslib-common/models/domain/logInCredentials"; +import { TokenRequestTwoFactor } from "jslib-common/models/request/identityToken/tokenRequestTwoFactor"; +import { TwoFactorEmailRequest } from "jslib-common/models/request/twoFactorEmailRequest"; +import { UpdateTempPasswordRequest } from "jslib-common/models/request/updateTempPasswordRequest"; +import { ErrorResponse } from "jslib-common/models/response/errorResponse"; + +import { Response } from "../models/response"; +import { MessageResponse } from "../models/response/messageResponse"; + +export class LoginCommand { + protected validatedParams: () => Promise; + protected success: () => Promise; + protected logout: () => Promise; + protected canInteract: boolean; + protected clientId: string; + protected clientSecret: string; + protected email: string; + + private ssoRedirectUri: string = null; + + constructor( + protected authService: AuthService, + protected apiService: ApiService, + protected i18nService: I18nService, + protected environmentService: EnvironmentService, + protected passwordGenerationService: PasswordGenerationService, + protected cryptoFunctionService: CryptoFunctionService, + protected platformUtilsService: PlatformUtilsService, + protected stateService: StateService, + protected cryptoService: CryptoService, + protected policyService: PolicyService, + protected twoFactorService: TwoFactorService, + clientId: string + ) { + this.clientId = clientId; + } + + async run(email: string, password: string, options: program.OptionValues) { + this.canInteract = process.env.BW_NOINTERACTION !== "true"; + + let ssoCodeVerifier: string = null; + let ssoCode: string = null; + let orgIdentifier: string = null; + + let clientId: string = null; + let clientSecret: string = null; + + let selectedProvider: any = null; + + if (options.apikey != null) { + const apiIdentifiers = await this.apiIdentifiers(); + clientId = apiIdentifiers.clientId; + clientSecret = apiIdentifiers.clientSecret; + } else if (options.sso != null && this.canInteract) { + const passwordOptions: any = { + type: "password", + length: 64, + uppercase: true, + lowercase: true, + numbers: true, + special: false, + }; + const state = await this.passwordGenerationService.generatePassword(passwordOptions); + ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions); + const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256"); + const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); + try { + const ssoParams = await this.openSsoPrompt(codeChallenge, state); + ssoCode = ssoParams.ssoCode; + orgIdentifier = ssoParams.orgIdentifier; + } catch { + return Response.badRequest("Something went wrong. Try again."); + } + } else { + if ((email == null || email === "") && this.canInteract) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "input", + name: "email", + message: "Email address:", + }); + email = answer.email; + } + if (email == null || email.trim() === "") { + return Response.badRequest("Email address is required."); + } + if (email.indexOf("@") === -1) { + return Response.badRequest("Email address is invalid."); + } + this.email = email; + + if (password == null || password === "") { + if (options.passwordfile) { + password = await NodeUtils.readFirstLine(options.passwordfile); + } else if (options.passwordenv && process.env[options.passwordenv]) { + password = process.env[options.passwordenv]; + } else if (this.canInteract) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "password", + name: "password", + message: "Master password:", + }); + password = answer.password; + } + } + + if (password == null || password === "") { + return Response.badRequest("Master password is required."); + } + } + + let twoFactorToken: string = options.code; + let twoFactorMethod: TwoFactorProviderType = null; + try { + if (options.method != null) { + twoFactorMethod = parseInt(options.method, null); + } + } catch (e) { + return Response.error("Invalid two-step login method."); + } + + const twoFactor = + twoFactorToken == null + ? null + : new TokenRequestTwoFactor(twoFactorMethod, twoFactorToken, false); + + try { + if (this.validatedParams != null) { + await this.validatedParams(); + } + + let response: AuthResult = null; + if (clientId != null && clientSecret != null) { + response = await this.authService.logIn(new ApiLogInCredentials(clientId, clientSecret)); + } else if (ssoCode != null && ssoCodeVerifier != null) { + response = await this.authService.logIn( + new SsoLogInCredentials( + ssoCode, + ssoCodeVerifier, + this.ssoRedirectUri, + orgIdentifier, + twoFactor + ) + ); + } else { + response = await this.authService.logIn( + new PasswordLogInCredentials(email, password, null, twoFactor) + ); + } + if (response.captchaSiteKey) { + const credentials = new PasswordLogInCredentials(email, password); + const handledResponse = await this.handleCaptchaRequired(twoFactor, credentials); + + // Error Response + if (handledResponse instanceof Response) { + return handledResponse; + } else { + response = handledResponse; + } + } + if (response.requiresTwoFactor) { + const twoFactorProviders = this.twoFactorService.getSupportedProviders(null); + if (twoFactorProviders.length === 0) { + return Response.badRequest("No providers available for this client."); + } + + if (twoFactorMethod != null) { + try { + selectedProvider = twoFactorProviders.filter((p) => p.type === twoFactorMethod)[0]; + } catch (e) { + return Response.error("Invalid two-step login method."); + } + } + + if (selectedProvider == null) { + if (twoFactorProviders.length === 1) { + selectedProvider = twoFactorProviders[0]; + } else if (this.canInteract) { + const twoFactorOptions: (string | Separator)[] = twoFactorProviders.map((p) => p.name); + twoFactorOptions.push(new inquirer.Separator()); + twoFactorOptions.push("Cancel"); + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "list", + name: "method", + message: "Two-step login method:", + choices: twoFactorOptions, + }); + const i = twoFactorOptions.indexOf(answer.method); + if (i === twoFactorOptions.length - 1) { + return Response.error("Login failed."); + } + selectedProvider = twoFactorProviders[i]; + } + if (selectedProvider == null) { + return Response.error("Login failed. No provider selected."); + } + } + + if ( + twoFactorToken == null && + response.twoFactorProviders.size > 1 && + selectedProvider.type === TwoFactorProviderType.Email + ) { + const emailReq = new TwoFactorEmailRequest(); + emailReq.email = this.authService.email; + emailReq.masterPasswordHash = this.authService.masterPasswordHash; + await this.apiService.postTwoFactorEmail(emailReq); + } + + if (twoFactorToken == null) { + if (this.canInteract) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "input", + name: "token", + message: "Two-step login code:", + }); + twoFactorToken = answer.token; + } + if (twoFactorToken == null || twoFactorToken === "") { + return Response.badRequest("Code is required."); + } + } + + response = await this.authService.logInTwoFactor( + new TokenRequestTwoFactor(selectedProvider.type, twoFactorToken), + null + ); + } + + if (response.captchaSiteKey) { + const twoFactorRequest = new TokenRequestTwoFactor(selectedProvider.type, twoFactorToken); + const handledResponse = await this.handleCaptchaRequired(twoFactorRequest); + + // Error Response + if (handledResponse instanceof Response) { + return handledResponse; + } else { + response = handledResponse; + } + } + + if (response.requiresTwoFactor) { + return Response.error("Login failed."); + } + + if (response.resetMasterPassword) { + return Response.error( + "In order to log in with SSO from the CLI, you must first log in" + + " through the web vault to set your master password." + ); + } + + // Handle Updating Temp Password if NOT using an API Key for authentication + if (response.forcePasswordReset && clientId == null && clientSecret == null) { + return await this.updateTempPassword(); + } + + return await this.handleSuccessResponse(); + } catch (e) { + return Response.error(e); + } + } + + private async handleSuccessResponse(): Promise { + if (this.success != null) { + const res = await this.success(); + return Response.success(res); + } else { + const res = new MessageResponse("You are logged in!", null); + return Response.success(res); + } + } + + private async updateTempPassword(error?: string): Promise { + // If no interaction available, alert user to use web vault + if (!this.canInteract) { + await this.logout(); + this.authService.logOut(() => { + /* Do nothing */ + }); + return Response.error( + new MessageResponse( + "An organization administrator recently changed your master password. In order to access the vault, you must update your master password now via the web vault. You have been logged out.", + null + ) + ); + } + + if (this.email == null || this.email === "undefined") { + this.email = await this.stateService.getEmail(); + } + + // Get New Master Password + const baseMessage = + "An organization administrator recently changed your master password. In order to access the vault, you must update your master password now.\n" + + "Master password: "; + const firstMessage = error != null ? error + baseMessage : baseMessage; + const mp: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ + type: "password", + name: "password", + message: firstMessage, + }); + const masterPassword = mp.password; + + // Master Password Validation + if (masterPassword == null || masterPassword === "") { + return this.updateTempPassword("Master password is required.\n"); + } + + if (masterPassword.length < 8) { + return this.updateTempPassword("Master password must be at least 8 characters long.\n"); + } + + // Strength & Policy Validation + const strengthResult = this.passwordGenerationService.passwordStrength( + masterPassword, + this.getPasswordStrengthUserInput() + ); + + // Get New Master Password Re-type + const reTypeMessage = "Re-type New Master password (Strength: " + strengthResult.score + ")"; + const retype: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ + type: "password", + name: "password", + message: reTypeMessage, + }); + const masterPasswordRetype = retype.password; + + // Re-type Validation + if (masterPassword !== masterPasswordRetype) { + return this.updateTempPassword("Master password confirmation does not match.\n"); + } + + // Get Hint (optional) + const hint: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ + type: "input", + name: "input", + message: "Master Password Hint (optional):", + }); + const masterPasswordHint = hint.input; + + // Retrieve details for key generation + const enforcedPolicyOptions = await this.policyService.getMasterPasswordPolicyOptions(); + const kdf = await this.stateService.getKdfType(); + const kdfIterations = await this.stateService.getKdfIterations(); + + if ( + enforcedPolicyOptions != null && + !this.policyService.evaluateMasterPassword( + strengthResult.score, + masterPassword, + enforcedPolicyOptions + ) + ) { + return this.updateTempPassword( + "Your new master password does not meet the policy requirements.\n" + ); + } + + try { + // Create new key and hash new password + const newKey = await this.cryptoService.makeKey( + masterPassword, + this.email.trim().toLowerCase(), + kdf, + kdfIterations + ); + const newPasswordHash = await this.cryptoService.hashPassword(masterPassword, newKey); + + // Grab user's current enc key + const userEncKey = await this.cryptoService.getEncKey(); + + // Create new encKey for the User + const newEncKey = await this.cryptoService.remakeEncKey(newKey, userEncKey); + + // Create request + const request = new UpdateTempPasswordRequest(); + request.key = newEncKey[1].encryptedString; + request.newMasterPasswordHash = newPasswordHash; + request.masterPasswordHint = masterPasswordHint; + + // Update user's password + await this.apiService.putUpdateTempPassword(request); + return this.handleSuccessResponse(); + } catch (e) { + await this.logout(); + this.authService.logOut(() => { + /* Do nothing */ + }); + return Response.error(e); + } + } + + private async handleCaptchaRequired( + twoFactorRequest: TokenRequestTwoFactor, + credentials: PasswordLogInCredentials = null + ): Promise { + const badCaptcha = Response.badRequest( + "Your authentication request has been flagged and will require user interaction to proceed.\n" + + "Please use your API key to validate this request and ensure BW_CLIENTSECRET is correct, if set.\n" + + "(https://bitwarden.com/help/cli-auth-challenges)" + ); + + try { + const captchaClientSecret = await this.apiClientSecret(true); + if (Utils.isNullOrWhitespace(captchaClientSecret)) { + return badCaptcha; + } + + let authResultResponse: AuthResult = null; + if (credentials != null) { + credentials.captchaToken = captchaClientSecret; + credentials.twoFactor = twoFactorRequest; + authResultResponse = await this.authService.logIn(credentials); + } else { + authResultResponse = await this.authService.logInTwoFactor( + twoFactorRequest, + captchaClientSecret + ); + } + + return authResultResponse; + } catch (e) { + if ( + e instanceof ErrorResponse || + (e.constructor.name === ErrorResponse.name && + (e as ErrorResponse).message.includes("Captcha is invalid")) + ) { + return badCaptcha; + } else { + return Response.error(e); + } + } + } + + private getPasswordStrengthUserInput() { + let userInput: string[] = []; + const atPosition = this.email.indexOf("@"); + if (atPosition > -1) { + userInput = userInput.concat( + this.email + .substr(0, atPosition) + .trim() + .toLowerCase() + .split(/[^A-Za-z0-9]/) + ); + } + return userInput; + } + + private async apiClientId(): Promise { + let clientId: string = null; + + const storedClientId: string = process.env.BW_CLIENTID; + if (storedClientId == null) { + if (this.canInteract) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "input", + name: "clientId", + message: "client_id:", + }); + clientId = answer.clientId; + } else { + clientId = null; + } + } else { + clientId = storedClientId; + } + + return clientId; + } + + private async apiClientSecret(isAdditionalAuthentication = false): Promise { + const additionalAuthenticationMessage = "Additional authentication required.\nAPI key "; + let clientSecret: string = null; + + const storedClientSecret: string = this.clientSecret || process.env.BW_CLIENTSECRET; + if (this.canInteract && storedClientSecret == null) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "input", + name: "clientSecret", + message: + (isAdditionalAuthentication ? additionalAuthenticationMessage : "") + "client_secret:", + }); + clientSecret = answer.clientSecret; + } else { + clientSecret = storedClientSecret; + } + + return clientSecret; + } + + private async apiIdentifiers(): Promise<{ clientId: string; clientSecret: string }> { + return { + clientId: await this.apiClientId(), + clientSecret: await this.apiClientSecret(), + }; + } + + private async openSsoPrompt( + codeChallenge: string, + state: string + ): Promise<{ ssoCode: string; orgIdentifier: string }> { + return new Promise((resolve, reject) => { + const callbackServer = http.createServer((req, res) => { + const urlString = "http://localhost" + req.url; + const url = new URL(urlString); + const code = url.searchParams.get("code"); + const receivedState = url.searchParams.get("state"); + const orgIdentifier = this.getOrgIdentifierFromState(receivedState); + res.setHeader("Content-Type", "text/html"); + if (code != null && receivedState != null && this.checkState(receivedState, state)) { + res.writeHead(200); + res.end( + "Success | Bitwarden CLI" + + "

Successfully authenticated with the Bitwarden CLI

" + + "

You may now close this tab and return to the terminal.

" + + "" + ); + callbackServer.close(() => + resolve({ + ssoCode: code, + orgIdentifier: orgIdentifier, + }) + ); + } else { + res.writeHead(400); + res.end( + "Failed | Bitwarden CLI" + + "

Something went wrong logging into the Bitwarden CLI

" + + "

You may now close this tab and return to the terminal.

" + + "" + ); + callbackServer.close(() => reject()); + } + }); + let foundPort = false; + const webUrl = this.environmentService.getWebVaultUrl(); + for (let port = 8065; port <= 8070; port++) { + try { + this.ssoRedirectUri = "http://localhost:" + port; + callbackServer.listen(port, () => { + this.platformUtilsService.launchUri( + webUrl + + "/#/sso?clientId=" + + this.clientId + + "&redirectUri=" + + encodeURIComponent(this.ssoRedirectUri) + + "&state=" + + state + + "&codeChallenge=" + + codeChallenge + ); + }); + foundPort = true; + break; + } catch { + // Ignore error since we run the same command up to 5 times. + } + } + if (!foundPort) { + reject(); + } + }); + } + + private getOrgIdentifierFromState(state: string): string { + if (state === null || state === undefined) { + return null; + } + + const stateSplit = state.split("_identifier="); + return stateSplit.length > 1 ? stateSplit[1] : null; + } + + private checkState(state: string, checkState: string): boolean { + if (state === null || state === undefined) { + return false; + } + if (checkState === null || checkState === undefined) { + return false; + } + + const stateSplit = state.split("_identifier="); + const checkStateSplit = checkState.split("_identifier="); + return stateSplit[0] === checkStateSplit[0]; + } +} diff --git a/jslib/node/src/cli/commands/logout.command.ts b/jslib/node/src/cli/commands/logout.command.ts new file mode 100644 index 00000000..6074ba9c --- /dev/null +++ b/jslib/node/src/cli/commands/logout.command.ts @@ -0,0 +1,22 @@ +import { AuthService } from "jslib-common/abstractions/auth.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +import { Response } from "../models/response"; +import { MessageResponse } from "../models/response/messageResponse"; + +export class LogoutCommand { + constructor( + private authService: AuthService, + private i18nService: I18nService, + private logoutCallback: () => Promise + ) {} + + async run() { + await this.logoutCallback(); + this.authService.logOut(() => { + /* Do nothing */ + }); + const res = new MessageResponse("You have logged out.", null); + return Response.success(res); + } +} diff --git a/jslib/node/src/cli/commands/update.command.ts b/jslib/node/src/cli/commands/update.command.ts new file mode 100644 index 00000000..1b9408a3 --- /dev/null +++ b/jslib/node/src/cli/commands/update.command.ts @@ -0,0 +1,104 @@ +import * as fetch from "node-fetch"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; + +import { Response } from "../models/response"; +import { MessageResponse } from "../models/response/messageResponse"; + +export class UpdateCommand { + inPkg = false; + + constructor( + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private repoName: string, + private executableName: string, + private showExtendedMessage: boolean + ) { + this.inPkg = !!(process as any).pkg; + } + + async run(): Promise { + const currentVersion = await this.platformUtilsService.getApplicationVersion(); + + const response = await fetch.default( + "https://api.github.com/repos/bitwarden/" + this.repoName + "/releases/latest" + ); + if (response.status === 200) { + const responseJson = await response.json(); + const res = new MessageResponse(null, null); + + const tagName: string = responseJson.tag_name; + if (tagName === "v" + currentVersion) { + res.title = "No update available."; + res.noColor = true; + return Response.success(res); + } + + let downloadUrl: string = null; + if (responseJson.assets != null) { + for (const a of responseJson.assets) { + const download: string = a.browser_download_url; + if (download == null) { + continue; + } + + if (download.indexOf(".zip") === -1) { + continue; + } + + if ( + process.platform === "win32" && + download.indexOf(this.executableName + "-windows") > -1 + ) { + downloadUrl = download; + break; + } else if ( + process.platform === "darwin" && + download.indexOf(this.executableName + "-macos") > -1 + ) { + downloadUrl = download; + break; + } else if ( + process.platform === "linux" && + download.indexOf(this.executableName + "-linux") > -1 + ) { + downloadUrl = download; + break; + } + } + } + + res.title = "A new version is available: " + tagName; + if (downloadUrl == null) { + downloadUrl = "https://github.com/bitwarden/" + this.repoName + "/releases"; + } else { + res.raw = downloadUrl; + } + res.message = ""; + if (responseJson.body != null && responseJson.body !== "") { + res.message = responseJson.body + "\n\n"; + } + + res.message += "You can download this update at " + downloadUrl; + + if (this.showExtendedMessage) { + if (this.inPkg) { + res.message += + "\n\nIf you installed this CLI through a package manager " + + "you should probably update using its update command instead."; + } else { + res.message += + "\n\nIf you installed this CLI through NPM " + + "you should update using `npm install -g @bitwarden/" + + this.repoName + + "`"; + } + } + return Response.success(res); + } else { + return Response.error("Error contacting update API: " + response.status); + } + } +} diff --git a/jslib/node/src/cli/models/response.ts b/jslib/node/src/cli/models/response.ts new file mode 100644 index 00000000..d768c51f --- /dev/null +++ b/jslib/node/src/cli/models/response.ts @@ -0,0 +1,50 @@ +import { BaseResponse } from "./response/baseResponse"; + +export class Response { + static error(error: any, data?: any): Response { + const res = new Response(); + res.success = false; + if (typeof error === "string") { + res.message = error; + } else { + res.message = + error.message != null + ? error.message + : error.toString() === "[object Object]" + ? JSON.stringify(error) + : error.toString(); + } + res.data = data; + return res; + } + + static notFound(): Response { + return Response.error("Not found."); + } + + static badRequest(message: string): Response { + return Response.error(message); + } + + static multipleResults(ids: string[]): Response { + let msg = + "More than one result was found. Try getting a specific object by `id` instead. " + + "The following objects were found:"; + ids.forEach((id) => { + msg += "\n" + id; + }); + return Response.error(msg, ids); + } + + static success(data?: BaseResponse): Response { + const res = new Response(); + res.success = true; + res.data = data; + return res; + } + + success: boolean; + message: string; + errorCode: number; + data: BaseResponse; +} diff --git a/jslib/node/src/cli/models/response/baseResponse.ts b/jslib/node/src/cli/models/response/baseResponse.ts new file mode 100644 index 00000000..b0cc57da --- /dev/null +++ b/jslib/node/src/cli/models/response/baseResponse.ts @@ -0,0 +1,3 @@ +export interface BaseResponse { + object: string; +} diff --git a/jslib/node/src/cli/models/response/fileResponse.ts b/jslib/node/src/cli/models/response/fileResponse.ts new file mode 100644 index 00000000..c16b56e0 --- /dev/null +++ b/jslib/node/src/cli/models/response/fileResponse.ts @@ -0,0 +1,13 @@ +import { BaseResponse } from "./baseResponse"; + +export class FileResponse implements BaseResponse { + object: string; + data: Buffer; + fileName: string; + + constructor(data: Buffer, fileName: string) { + this.object = "file"; + this.data = data; + this.fileName = fileName; + } +} diff --git a/jslib/node/src/cli/models/response/listResponse.ts b/jslib/node/src/cli/models/response/listResponse.ts new file mode 100644 index 00000000..db415bcd --- /dev/null +++ b/jslib/node/src/cli/models/response/listResponse.ts @@ -0,0 +1,11 @@ +import { BaseResponse } from "./baseResponse"; + +export class ListResponse implements BaseResponse { + object: string; + data: BaseResponse[]; + + constructor(data: BaseResponse[]) { + this.object = "list"; + this.data = data; + } +} diff --git a/jslib/node/src/cli/models/response/messageResponse.ts b/jslib/node/src/cli/models/response/messageResponse.ts new file mode 100644 index 00000000..8612841a --- /dev/null +++ b/jslib/node/src/cli/models/response/messageResponse.ts @@ -0,0 +1,15 @@ +import { BaseResponse } from "./baseResponse"; + +export class MessageResponse implements BaseResponse { + object: string; + title: string; + message: string; + raw: string; + noColor = false; + + constructor(title: string, message: string) { + this.object = "message"; + this.title = title; + this.message = message; + } +} diff --git a/jslib/node/src/cli/models/response/stringResponse.ts b/jslib/node/src/cli/models/response/stringResponse.ts new file mode 100644 index 00000000..f4becfe8 --- /dev/null +++ b/jslib/node/src/cli/models/response/stringResponse.ts @@ -0,0 +1,11 @@ +import { BaseResponse } from "./baseResponse"; + +export class StringResponse implements BaseResponse { + object: string; + data: string; + + constructor(data: string) { + this.object = "string"; + this.data = data; + } +} diff --git a/jslib/node/src/cli/services/cliPlatformUtils.service.ts b/jslib/node/src/cli/services/cliPlatformUtils.service.ts new file mode 100644 index 00000000..520170c2 --- /dev/null +++ b/jslib/node/src/cli/services/cliPlatformUtils.service.ts @@ -0,0 +1,166 @@ +import * as child_process from "child_process"; + +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { ClientType } from "jslib-common/enums/clientType"; +import { DeviceType } from "jslib-common/enums/deviceType"; +import { ThemeType } from "jslib-common/enums/themeType"; + +// eslint-disable-next-line +const open = require("open"); + +export class CliPlatformUtilsService implements PlatformUtilsService { + clientType: ClientType; + + private deviceCache: DeviceType = null; + + constructor(clientType: ClientType, private packageJson: any) { + this.clientType = clientType; + } + + getDevice(): DeviceType { + if (!this.deviceCache) { + switch (process.platform) { + case "win32": + this.deviceCache = DeviceType.WindowsDesktop; + break; + case "darwin": + this.deviceCache = DeviceType.MacOsDesktop; + break; + case "linux": + default: + this.deviceCache = DeviceType.LinuxDesktop; + break; + } + } + + return this.deviceCache; + } + + getDeviceString(): string { + const device = DeviceType[this.getDevice()].toLowerCase(); + return device.replace("desktop", ""); + } + + getClientType() { + return this.clientType; + } + + isFirefox() { + return false; + } + + isChrome() { + return false; + } + + isEdge() { + return false; + } + + isOpera() { + return false; + } + + isVivaldi() { + return false; + } + + isSafari() { + return false; + } + + isMacAppStore() { + return false; + } + + isViewOpen() { + return Promise.resolve(false); + } + + launchUri(uri: string, options?: any): void { + if (process.platform === "linux") { + child_process.spawnSync("xdg-open", [uri]); + } else { + open(uri); + } + } + + saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void { + throw new Error("Not implemented."); + } + + getApplicationVersion(): Promise { + return Promise.resolve(this.packageJson.version); + } + + getApplicationVersionSync(): string { + return this.packageJson.version; + } + + supportsWebAuthn(win: Window) { + return false; + } + + supportsDuo(): boolean { + return false; + } + + showToast( + type: "error" | "success" | "warning" | "info", + title: string, + text: string | string[], + options?: any + ): void { + throw new Error("Not implemented."); + } + + showDialog( + text: string, + title?: string, + confirmText?: string, + cancelText?: string, + type?: string + ): Promise { + throw new Error("Not implemented."); + } + + isDev(): boolean { + return process.env.BWCLI_ENV === "development"; + } + + isSelfHost(): boolean { + return false; + } + + copyToClipboard(text: string, options?: any): void { + throw new Error("Not implemented."); + } + + readFromClipboard(options?: any): Promise { + throw new Error("Not implemented."); + } + + supportsBiometric(): Promise { + return Promise.resolve(false); + } + + authenticateBiometric(): Promise { + return Promise.resolve(false); + } + + getDefaultSystemTheme() { + return Promise.resolve(ThemeType.Light as ThemeType.Light | ThemeType.Dark); + } + + onDefaultSystemThemeChange() { + /* noop */ + } + + getEffectiveTheme() { + return Promise.resolve(ThemeType.Light); + } + + supportsSecureStorage(): boolean { + return false; + } +} diff --git a/jslib/node/src/cli/services/consoleLog.service.ts b/jslib/node/src/cli/services/consoleLog.service.ts new file mode 100644 index 00000000..73dfcc55 --- /dev/null +++ b/jslib/node/src/cli/services/consoleLog.service.ts @@ -0,0 +1,22 @@ +import { LogLevelType } from "jslib-common/enums/logLevelType"; +import { ConsoleLogService as BaseConsoleLogService } from "jslib-common/services/consoleLog.service"; + +export class ConsoleLogService extends BaseConsoleLogService { + constructor(isDev: boolean, filter: (level: LogLevelType) => boolean = null) { + super(isDev, filter); + } + + write(level: LogLevelType, message: string) { + if (this.filter != null && this.filter(level)) { + return; + } + + if (process.env.BW_RESPONSE === "true") { + // eslint-disable-next-line + console.error(message); + return; + } + + super.write(level, message); + } +} diff --git a/jslib/node/src/globals.d.ts b/jslib/node/src/globals.d.ts new file mode 100644 index 00000000..e69de29b diff --git a/jslib/node/src/services/lowdbStorage.service.ts b/jslib/node/src/services/lowdbStorage.service.ts new file mode 100644 index 00000000..e78fe2e4 --- /dev/null +++ b/jslib/node/src/services/lowdbStorage.service.ts @@ -0,0 +1,148 @@ +import * as fs from "fs"; +import * as path from "path"; + +import * as lowdb from "lowdb"; +import * as FileSync from "lowdb/adapters/FileSync"; + +import { LogService } from "jslib-common/abstractions/log.service"; +import { StorageService } from "jslib-common/abstractions/storage.service"; +import { NodeUtils } from "jslib-common/misc/nodeUtils"; +import { sequentialize } from "jslib-common/misc/sequentialize"; +import { Utils } from "jslib-common/misc/utils"; + +export class LowdbStorageService implements StorageService { + protected dataFilePath: string; + private db: lowdb.LowdbSync; + private defaults: any; + private ready = false; + + constructor( + protected logService: LogService, + defaults?: any, + private dir?: string, + private allowCache = false + ) { + this.defaults = defaults; + } + + @sequentialize(() => "lowdbStorageInit") + async init() { + if (this.ready) { + return; + } + + this.logService.info("Initializing lowdb storage service."); + let adapter: lowdb.AdapterSync; + if (Utils.isNode && this.dir != null) { + if (!fs.existsSync(this.dir)) { + this.logService.warning(`Could not find dir, "${this.dir}"; creating it instead.`); + NodeUtils.mkdirpSync(this.dir, "700"); + this.logService.info(`Created dir "${this.dir}".`); + } + this.dataFilePath = path.join(this.dir, "data.json"); + if (!fs.existsSync(this.dataFilePath)) { + this.logService.warning( + `Could not find data file, "${this.dataFilePath}"; creating it instead.` + ); + fs.writeFileSync(this.dataFilePath, "", { mode: 0o600 }); + fs.chmodSync(this.dataFilePath, 0o600); + this.logService.info(`Created data file "${this.dataFilePath}" with chmod 600.`); + } else { + this.logService.info(`db file "${this.dataFilePath} already exists"; using existing db`); + } + await this.lockDbFile(() => { + adapter = new FileSync(this.dataFilePath); + }); + } + try { + this.logService.info("Attempting to create lowdb storage adapter."); + this.db = lowdb(adapter); + this.logService.info("Successfully created lowdb storage adapter."); + } catch (e) { + if (e instanceof SyntaxError) { + this.logService.warning( + `Error creating lowdb storage adapter, "${e.message}"; emptying data file.` + ); + if (fs.existsSync(this.dataFilePath)) { + const backupPath = this.dataFilePath + ".bak"; + this.logService.warning(`Writing backup of data file to ${backupPath}`); + await fs.copyFile(this.dataFilePath, backupPath, () => { + this.logService.warning( + `Error while creating data file backup, "${e.message}". No backup may have been created.` + ); + }); + } + adapter.write({}); + this.db = lowdb(adapter); + } else { + this.logService.error(`Error creating lowdb storage adapter, "${e.message}".`); + throw e; + } + } + + if (this.defaults != null) { + this.lockDbFile(() => { + this.logService.info("Writing defaults."); + this.readForNoCache(); + this.db.defaults(this.defaults).write(); + this.logService.info("Successfully wrote defaults to db."); + }); + } + + this.ready = true; + } + + async get(key: string): Promise { + await this.waitForReady(); + return this.lockDbFile(() => { + this.readForNoCache(); + const val = this.db.get(key).value(); + this.logService.debug(`Successfully read ${key} from db`); + if (val == null) { + return null; + } + return val as T; + }); + } + + has(key: string): Promise { + return this.get(key).then((v) => v != null); + } + + async save(key: string, obj: any): Promise { + await this.waitForReady(); + return this.lockDbFile(() => { + this.readForNoCache(); + this.db.set(key, obj).write(); + this.logService.debug(`Successfully wrote ${key} to db`); + return; + }); + } + + async remove(key: string): Promise { + await this.waitForReady(); + return this.lockDbFile(() => { + this.readForNoCache(); + this.db.unset(key).write(); + this.logService.debug(`Successfully removed ${key} from db`); + return; + }); + } + + protected async lockDbFile(action: () => T): Promise { + // Lock methods implemented in clients + return Promise.resolve(action()); + } + + private readForNoCache() { + if (!this.allowCache) { + this.db.read(); + } + } + + private async waitForReady() { + if (!this.ready) { + await this.init(); + } + } +} diff --git a/jslib/node/src/services/nodeApi.service.ts b/jslib/node/src/services/nodeApi.service.ts new file mode 100644 index 00000000..7dd03412 --- /dev/null +++ b/jslib/node/src/services/nodeApi.service.ts @@ -0,0 +1,43 @@ +import * as FormData from "form-data"; +import { HttpsProxyAgent } from "https-proxy-agent"; +import * as fe from "node-fetch"; + +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { ApiService } from "jslib-common/services/api.service"; + +(global as any).fetch = fe.default; +(global as any).Request = fe.Request; +(global as any).Response = fe.Response; +(global as any).Headers = fe.Headers; +(global as any).FormData = FormData; + +export class NodeApiService extends ApiService { + constructor( + tokenService: TokenService, + platformUtilsService: PlatformUtilsService, + environmentService: EnvironmentService, + appIdService: AppIdService, + logoutCallback: (expired: boolean) => Promise, + customUserAgent: string = null + ) { + super( + tokenService, + platformUtilsService, + environmentService, + appIdService, + logoutCallback, + customUserAgent + ); + } + + nativeFetch(request: Request): Promise { + const proxy = process.env.http_proxy || process.env.https_proxy; + if (proxy) { + (request as any).agent = new HttpsProxyAgent(proxy); + } + return fetch(request); + } +} diff --git a/jslib/node/src/services/nodeCryptoFunction.service.ts b/jslib/node/src/services/nodeCryptoFunction.service.ts new file mode 100644 index 00000000..90a04574 --- /dev/null +++ b/jslib/node/src/services/nodeCryptoFunction.service.ts @@ -0,0 +1,301 @@ +import * as crypto from "crypto"; + +import * as forge from "node-forge"; + +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { Utils } from "jslib-common/misc/utils"; +import { DecryptParameters } from "jslib-common/models/domain/decryptParameters"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; + +export class NodeCryptoFunctionService implements CryptoFunctionService { + pbkdf2( + password: string | ArrayBuffer, + salt: string | ArrayBuffer, + algorithm: "sha256" | "sha512", + iterations: number + ): Promise { + const len = algorithm === "sha256" ? 32 : 64; + const nodePassword = this.toNodeValue(password); + const nodeSalt = this.toNodeValue(salt); + return new Promise((resolve, reject) => { + crypto.pbkdf2(nodePassword, nodeSalt, iterations, len, algorithm, (error, key) => { + if (error != null) { + reject(error); + } else { + resolve(this.toArrayBuffer(key)); + } + }); + }); + } + + // ref: https://tools.ietf.org/html/rfc5869 + async hkdf( + ikm: ArrayBuffer, + salt: string | ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ): Promise { + const saltBuf = this.toArrayBuffer(salt); + const prk = await this.hmac(ikm, saltBuf, algorithm); + return this.hkdfExpand(prk, info, outputByteSize, algorithm); + } + + // ref: https://tools.ietf.org/html/rfc5869 + async hkdfExpand( + prk: ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ): Promise { + const hashLen = algorithm === "sha256" ? 32 : 64; + if (outputByteSize > 255 * hashLen) { + throw new Error("outputByteSize is too large."); + } + const prkArr = new Uint8Array(prk); + if (prkArr.length < hashLen) { + throw new Error("prk is too small."); + } + const infoBuf = this.toArrayBuffer(info); + const infoArr = new Uint8Array(infoBuf); + let runningOkmLength = 0; + let previousT = new Uint8Array(0); + const n = Math.ceil(outputByteSize / hashLen); + const okm = new Uint8Array(n * hashLen); + for (let i = 0; i < n; i++) { + const t = new Uint8Array(previousT.length + infoArr.length + 1); + t.set(previousT); + t.set(infoArr, previousT.length); + t.set([i + 1], t.length - 1); + previousT = new Uint8Array(await this.hmac(t.buffer, prk, algorithm)); + okm.set(previousT, runningOkmLength); + runningOkmLength += previousT.length; + if (runningOkmLength >= outputByteSize) { + break; + } + } + return okm.slice(0, outputByteSize).buffer; + } + + hash( + value: string | ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" | "md5" + ): Promise { + const nodeValue = this.toNodeValue(value); + const hash = crypto.createHash(algorithm); + hash.update(nodeValue); + return Promise.resolve(this.toArrayBuffer(hash.digest())); + } + + hmac( + value: ArrayBuffer, + key: ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" + ): Promise { + const nodeValue = this.toNodeBuffer(value); + const nodeKey = this.toNodeBuffer(key); + const hmac = crypto.createHmac(algorithm, nodeKey); + hmac.update(nodeValue); + return Promise.resolve(this.toArrayBuffer(hmac.digest())); + } + + async compare(a: ArrayBuffer, b: ArrayBuffer): Promise { + const key = await this.randomBytes(32); + const mac1 = await this.hmac(a, key, "sha256"); + const mac2 = await this.hmac(b, key, "sha256"); + if (mac1.byteLength !== mac2.byteLength) { + return false; + } + + const arr1 = new Uint8Array(mac1); + const arr2 = new Uint8Array(mac2); + for (let i = 0; i < arr2.length; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + + return true; + } + + hmacFast( + value: ArrayBuffer, + key: ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" + ): Promise { + return this.hmac(value, key, algorithm); + } + + compareFast(a: ArrayBuffer, b: ArrayBuffer): Promise { + return this.compare(a, b); + } + + aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const nodeData = this.toNodeBuffer(data); + const nodeIv = this.toNodeBuffer(iv); + const nodeKey = this.toNodeBuffer(key); + const cipher = crypto.createCipheriv("aes-256-cbc", nodeKey, nodeIv); + const encBuf = Buffer.concat([cipher.update(nodeData), cipher.final()]); + return Promise.resolve(this.toArrayBuffer(encBuf)); + } + + aesDecryptFastParameters( + data: string, + iv: string, + mac: string, + key: SymmetricCryptoKey + ): DecryptParameters { + const p = new DecryptParameters(); + p.encKey = key.encKey; + p.data = Utils.fromB64ToArray(data).buffer; + p.iv = Utils.fromB64ToArray(iv).buffer; + + const macData = new Uint8Array(p.iv.byteLength + p.data.byteLength); + macData.set(new Uint8Array(p.iv), 0); + macData.set(new Uint8Array(p.data), p.iv.byteLength); + p.macData = macData.buffer; + + if (key.macKey != null) { + p.macKey = key.macKey; + } + if (mac != null) { + p.mac = Utils.fromB64ToArray(mac).buffer; + } + + return p; + } + + async aesDecryptFast(parameters: DecryptParameters): Promise { + const decBuf = await this.aesDecrypt(parameters.data, parameters.iv, parameters.encKey); + return Utils.fromBufferToUtf8(decBuf); + } + + aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const nodeData = this.toNodeBuffer(data); + const nodeIv = this.toNodeBuffer(iv); + const nodeKey = this.toNodeBuffer(key); + const decipher = crypto.createDecipheriv("aes-256-cbc", nodeKey, nodeIv); + const decBuf = Buffer.concat([decipher.update(nodeData), decipher.final()]); + return Promise.resolve(this.toArrayBuffer(decBuf)); + } + + rsaEncrypt( + data: ArrayBuffer, + publicKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ): Promise { + if (algorithm === "sha256") { + throw new Error("Node crypto does not support RSA-OAEP SHA-256"); + } + + const pem = this.toPemPublicKey(publicKey); + const decipher = crypto.publicEncrypt(pem, this.toNodeBuffer(data)); + return Promise.resolve(this.toArrayBuffer(decipher)); + } + + rsaDecrypt( + data: ArrayBuffer, + privateKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ): Promise { + if (algorithm === "sha256") { + throw new Error("Node crypto does not support RSA-OAEP SHA-256"); + } + + const pem = this.toPemPrivateKey(privateKey); + const decipher = crypto.privateDecrypt(pem, this.toNodeBuffer(data)); + return Promise.resolve(this.toArrayBuffer(decipher)); + } + + rsaExtractPublicKey(privateKey: ArrayBuffer): Promise { + const privateKeyByteString = Utils.fromBufferToByteString(privateKey); + const privateKeyAsn1 = forge.asn1.fromDer(privateKeyByteString); + const forgePrivateKey: any = forge.pki.privateKeyFromAsn1(privateKeyAsn1); + const forgePublicKey = (forge.pki as any).setRsaPublicKey(forgePrivateKey.n, forgePrivateKey.e); + const publicKeyAsn1 = forge.pki.publicKeyToAsn1(forgePublicKey); + const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).data; + const publicKeyArray = Utils.fromByteStringToArray(publicKeyByteString); + return Promise.resolve(publicKeyArray.buffer); + } + + async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[ArrayBuffer, ArrayBuffer]> { + return new Promise<[ArrayBuffer, ArrayBuffer]>((resolve, reject) => { + forge.pki.rsa.generateKeyPair( + { + bits: length, + workers: -1, + e: 0x10001, // 65537 + }, + (error, keyPair) => { + if (error != null) { + reject(error); + return; + } + + const publicKeyAsn1 = forge.pki.publicKeyToAsn1(keyPair.publicKey); + const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).getBytes(); + const publicKey = Utils.fromByteStringToArray(publicKeyByteString); + + const privateKeyAsn1 = forge.pki.privateKeyToAsn1(keyPair.privateKey); + const privateKeyPkcs8 = forge.pki.wrapRsaPrivateKey(privateKeyAsn1); + const privateKeyByteString = forge.asn1.toDer(privateKeyPkcs8).getBytes(); + const privateKey = Utils.fromByteStringToArray(privateKeyByteString); + + resolve([publicKey.buffer, privateKey.buffer]); + } + ); + }); + } + + randomBytes(length: number): Promise { + return new Promise((resolve, reject) => { + crypto.randomBytes(length, (error, bytes) => { + if (error != null) { + reject(error); + } else { + resolve(this.toArrayBuffer(bytes)); + } + }); + }); + } + + private toNodeValue(value: string | ArrayBuffer): string | Buffer { + let nodeValue: string | Buffer; + if (typeof value === "string") { + nodeValue = value; + } else { + nodeValue = this.toNodeBuffer(value); + } + return nodeValue; + } + + private toNodeBuffer(value: ArrayBuffer): Buffer { + return Buffer.from(new Uint8Array(value) as any); + } + + private toArrayBuffer(value: Buffer | string | ArrayBuffer): ArrayBuffer { + let buf: ArrayBuffer; + if (typeof value === "string") { + buf = Utils.fromUtf8ToArray(value).buffer; + } else { + buf = new Uint8Array(value).buffer; + } + return buf; + } + + private toPemPrivateKey(key: ArrayBuffer): string { + const byteString = Utils.fromBufferToByteString(key); + const asn1 = forge.asn1.fromDer(byteString); + const privateKey = forge.pki.privateKeyFromAsn1(asn1); + const rsaPrivateKey = forge.pki.privateKeyToAsn1(privateKey); + const privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey); + return forge.pki.privateKeyInfoToPem(privateKeyInfo); + } + + private toPemPublicKey(key: ArrayBuffer): string { + const byteString = Utils.fromBufferToByteString(key); + const asn1 = forge.asn1.fromDer(byteString); + const publicKey = forge.pki.publicKeyFromAsn1(asn1); + return forge.pki.publicKeyToPem(publicKey); + } +} diff --git a/jslib/node/tsconfig.json b/jslib/node/tsconfig.json new file mode 100644 index 00000000..f544d7ce --- /dev/null +++ b/jslib/node/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../shared/tsconfig", + "compilerOptions": { + "paths": { + "jslib-common/*": ["../common/src/*"], + "jslib-node/*": ["./src/*"] + } + }, + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/node/tsconfig.spec.json b/jslib/node/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/node/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/jslib/package-lock.json b/jslib/package-lock.json new file mode 100644 index 00000000..40597c1b --- /dev/null +++ b/jslib/package-lock.json @@ -0,0 +1,36691 @@ +{ + "name": "@bitwarden/jslib", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-angular": "file:angular", + "@bitwarden/jslib-common": "file:common", + "@bitwarden/jslib-electron": "file:electron", + "@bitwarden/jslib-node": "file:node" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^12.2.13", + "@angular/compiler-cli": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@fluffy-spoon/substitute": "^1.202.0", + "@types/jest": "^27.4.1", + "@types/node": "^16.11.12", + "@typescript-eslint/eslint-plugin": "^5.10.1", + "@typescript-eslint/parser": "^5.10.1", + "commander": "7.2.0", + "concurrently": "^6.1.0", + "eslint": "^8.7.0", + "eslint-config-prettier": "^8.3.0", + "eslint-import-resolver-typescript": "^2.5.0", + "eslint-plugin-import": "^2.25.4", + "form-data": "4.0.0", + "husky": "^7.0.4", + "jest": "^27.5.1", + "jest-preset-angular": "^11.1.1", + "jsdom": "^16.5.3", + "lint-staged": "^12.1.2", + "node-forge": "^1.2.0", + "nodemon": "^2.0.7", + "prettier": "2.5.1", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "ts-loader": "^8.1.0", + "ts-node": "^10.4.0", + "tsconfig-paths": "^3.12.0", + "ttypescript": "^1.5.12", + "typemoq": "^2.1.0", + "typescript": "4.3.5", + "typescript-transform-paths": "^2.2.3", + "zone.js": "0.11.4" + }, + "engines": { + "node": "~16", + "npm": "~8" + } + }, + "angular": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@angular/animations": "^12.2.13", + "@angular/cdk": "^12.2.13", + "@angular/common": "^12.2.13", + "@angular/compiler": "^12.2.13", + "@angular/core": "^12.2.13", + "@angular/forms": "^12.2.13", + "@angular/platform-browser": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@angular/router": "^12.2.13", + "@bitwarden/jslib-common": "file:../common", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zone.js": "0.11.4" + }, + "devDependencies": { + "@types/duo_web_sdk": "^2.7.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "common": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "electron": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "electron": "16.1.0", + "electron-log": "4.4.6", + "electron-store": "8.0.1", + "electron-updater": "5.0.0" + }, + "devDependencies": { + "@types/node": "^16.11.12", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "chalk": "^4.1.1", + "commander": "7.2.0", + "form-data": "4.0.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.0.0", + "lowdb": "1.0.0", + "node-fetch": "^2.6.1" + }, + "devDependencies": { + "@types/inquirer": "^7.3.1", + "@types/lowdb": "^1.0.10", + "@types/node": "^16.11.12", + "@types/node-fetch": "^2.5.10", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.0.1.tgz", + "integrity": "sha512-Ta9bMA3EtUHDaZJXqUoT5cn/EecwOp+SXpKJqxDbDuMbLvEMu6YTyDDuvTWeStODfdmXyfMo7LymQyPkN3BicA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "1.0.0", + "sourcemap-codec": "1.4.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.17.tgz", + "integrity": "sha512-uUQcHcLbPvr9adALQSLU1MTDduVUR2kZAHi2e7SmL9ioel84pPVXBoD0WpSBeUMKwPiDs3TQDaxDB49hl0nBSQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "12.2.17", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.17.tgz", + "integrity": "sha512-uc3HGHVQyatqQ/M53oxYBvhz0jx0hgdc7WT+L56GLHvgz7Ct2VEbpWaMfwHkFfE1F1iHkIgnTKHKWacJl1yQIg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "1.0.1", + "@angular-devkit/architect": "0.1202.17", + "@angular-devkit/build-optimizer": "0.1202.17", + "@angular-devkit/build-webpack": "0.1202.17", + "@angular-devkit/core": "12.2.17", + "@babel/core": "7.14.8", + "@babel/generator": "7.14.8", + "@babel/helper-annotate-as-pure": "7.14.5", + "@babel/plugin-proposal-async-generator-functions": "7.14.7", + "@babel/plugin-transform-async-to-generator": "7.14.5", + "@babel/plugin-transform-runtime": "7.14.5", + "@babel/preset-env": "7.14.8", + "@babel/runtime": "7.14.8", + "@babel/template": "7.14.5", + "@discoveryjs/json-ext": "0.5.3", + "@jsdevtools/coverage-istanbul-loader": "3.0.5", + "@ngtools/webpack": "12.2.17", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.2", + "browserslist": "^4.9.1", + "cacache": "15.2.0", + "caniuse-lite": "^1.0.30001032", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "9.0.1", + "core-js": "3.16.0", + "critters": "0.0.12", + "css-loader": "6.2.0", + "css-minimizer-webpack-plugin": "3.0.2", + "esbuild-wasm": "0.13.8", + "find-cache-dir": "3.3.1", + "glob": "7.1.7", + "https-proxy-agent": "5.0.0", + "inquirer": "8.1.2", + "karma-source-map-support": "1.4.0", + "less": "4.1.1", + "less-loader": "10.0.1", + "license-webpack-plugin": "2.3.20", + "loader-utils": "2.0.0", + "mini-css-extract-plugin": "2.4.2", + "minimatch": "3.0.4", + "open": "8.2.1", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.1.0", + "postcss": "8.3.6", + "postcss-import": "14.0.2", + "postcss-loader": "6.1.1", + "postcss-preset-env": "6.7.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "4.0.0", + "rxjs": "6.6.7", + "sass": "1.36.0", + "sass-loader": "12.1.0", + "semver": "7.3.5", + "source-map-loader": "3.0.0", + "source-map-support": "0.5.19", + "style-loader": "3.2.1", + "stylus": "0.54.8", + "stylus-loader": "6.1.0", + "terser": "5.7.1", + "terser-webpack-plugin": "5.1.4", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.0", + "webpack": "5.50.0", + "webpack-dev-middleware": "5.0.0", + "webpack-dev-server": "3.11.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "1.5.2" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.13.8" + }, + "peerDependencies": { + "@angular/compiler-cli": "^12.0.0", + "@angular/localize": "^12.0.0", + "@angular/service-worker": "^12.0.0", + "karma": "^6.3.0", + "ng-packagr": "^12.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0", + "tslint": "^6.1.0", + "typescript": "~4.2.3 || ~4.3.2" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + }, + "tslint": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/es-module-lexer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz", + "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack": { + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.50.0.tgz", + "integrity": "sha512-hqxI7t/KVygs0WRv/kTgUW8Kl3YC81uyWQSo/7WUs5LsuRw0htH/fCwbVBGCuiX/t4s7qzjXFcf41O8Reiypag==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.0", + "es-module-lexer": "^0.7.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.2.0", + "webpack-sources": "^3.2.0" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@angular-devkit/build-optimizer": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.17.tgz", + "integrity": "sha512-1qWGWw7cCNADB4LZ/zjiSK0GLmr2kebYyNG0KutCE8GNVxv2h6w6dJP6t1C/BgskRuBPCAhvE+lEKN8ljSutag==", + "dev": true, + "dependencies": { + "source-map": "0.7.3", + "tslib": "2.3.0", + "typescript": "4.3.5" + }, + "bin": { + "build-optimizer": "src/build-optimizer/cli.js" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.17.tgz", + "integrity": "sha512-z7FW43DJ4p8UZwbFRmMrh2ohqhI2Wtdg3+FZiTnl4opb3zYheGiNxPlTuiyKjG21JUkGCdthkkBLCNfaUU0U/Q==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1202.17", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^3.1.4" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/core": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.17.tgz", + "integrity": "sha512-PyOY7LGUPPd6rakxUYbfQN6zAdOCMCouVp5tERY1WTdMdEiuULOtHsPee8kNbh75pD59KbJNU+fwozPRMuIm5g==", + "dev": true, + "dependencies": { + "ajv": "8.6.2", + "ajv-formats": "2.1.0", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular/animations": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.16.tgz", + "integrity": "sha512-Kf6C7Ta+fCMq5DvT9JNVhBkcECrqFa3wumiC6ssGo5sNaEzXz+tlep9ZgEbqfxSn7gAN7L1DgsbS9u0O6tbUkg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/core": "12.2.16" + } + }, + "node_modules/@angular/cdk": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz", + "integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^12.0.0 || ^13.0.0-0", + "@angular/core": "^12.0.0 || ^13.0.0-0", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/cdk/node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "node_modules/@angular/common": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.16.tgz", + "integrity": "sha512-FEqTXTEsnbDInqV1yFlm97Tz1OFqZS5t0TUkm8gzXRgpIce/F/jLwAg0u1VQkgOsno6cNm0xTWPoZgu85NI4ug==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/core": "12.2.16", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/compiler": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.16.tgz", + "integrity": "sha512-nsYEw+yu8QyeqPf9nAmG419i1mtGM4v8+U+S3eQHQFXTgJzLymMykWHYu2ETdjUpNSLK6xcIQDBWtWnWSfJjAA==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.16.tgz", + "integrity": "sha512-tlalh8SJvdCWbUPRUR5GamaP+wSc/GuCsoUZpSbcczGKgSlbaEVXUYtVXm8/wuT6Slk2sSEbRs7tXGF2i7qxVw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.6", + "@babel/types": "^7.8.6", + "canonical-path": "1.0.0", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "source-map": "^0.6.1", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.2.0", + "yargs": "^17.0.0" + }, + "bin": { + "ivy-ngcc": "ngcc/main-ivy-ngcc.js", + "ng-xi18n": "src/extract_i18n.js", + "ngc": "src/main.js", + "ngcc": "ngcc/main-ngcc.js" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/compiler": "12.2.16", + "typescript": ">=4.2.3 <4.4" + } + }, + "node_modules/@angular/compiler-cli/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@angular/core": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.16.tgz", + "integrity": "sha512-jsmvaRdAfng99z2a9mAmkfcsCE1wm+tBYVDxnc5JquSXznwtncjzcoc2X0J0dzrkCDvzFfpTsZ9vehylytBc+A==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.0.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/forms": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.16.tgz", + "integrity": "sha512-sb+gpNun5aN7CZfHXS6X7vJcd/0A1P/gRBZpYtQTzBYnqEFCOFIvR62eb05aHQ4JhgKaSPpIXrbz/bAwY/njZw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.16", + "@angular/core": "12.2.16", + "@angular/platform-browser": "12.2.16", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.16.tgz", + "integrity": "sha512-T855ppLeQO6hRHi7lGf5fwPoUVt+c0h2rgkV5jHElc3ylaGnhecmZc6fnWLX4pw82TMJUgUV88CY8JCFabJWwg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/animations": "12.2.16", + "@angular/common": "12.2.16", + "@angular/core": "12.2.16" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.16.tgz", + "integrity": "sha512-XGxoACAMW/bc3atiVRpaiYwU4LkobYwVzwlxTT/BxOfsdt8ILb5wU8Fx1TMKNECOQHSGdK0qqhch4pTBZ3cb2g==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.16", + "@angular/compiler": "12.2.16", + "@angular/core": "12.2.16", + "@angular/platform-browser": "12.2.16" + } + }, + "node_modules/@angular/router": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.16.tgz", + "integrity": "sha512-LuFXSMIvX/VrB4jbYhigG2Y2pGQ9ULsSBUwDWwQCf4kr0eVI37LBJ2Vr74GBEznjgQ0UmWE89E+XYI80UhERTw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.16", + "@angular/core": "12.2.16", + "@angular/platform-browser": "12.2.16", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", + "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.8", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.8", + "@babel/helpers": "^7.14.8", + "@babel/parser": "^7.14.8", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", + "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz", + "integrity": "sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", + "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.4.tgz", + "integrity": "sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz", + "integrity": "sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", + "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz", + "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "dev": true, + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz", + "integrity": "sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.2", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.8.tgz", + "integrity": "sha512-a9aOppDU93oArQ51H+B8M1vH+tayZbuBqzjOhntGetZVa+4tTu5jp+XTwqHGG2lxslqomPYVSjIxQkFwXzgnxg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.14.7", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-async-generator-functions": "^7.14.7", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-class-static-block": "^7.14.5", + "@babel/plugin-proposal-dynamic-import": "^7.14.5", + "@babel/plugin-proposal-export-namespace-from": "^7.14.5", + "@babel/plugin-proposal-json-strings": "^7.14.5", + "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", + "@babel/plugin-proposal-numeric-separator": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.14.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", + "@babel/plugin-proposal-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-private-methods": "^7.14.5", + "@babel/plugin-proposal-private-property-in-object": "^7.14.5", + "@babel/plugin-proposal-unicode-property-regex": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.14.5", + "@babel/plugin-transform-async-to-generator": "^7.14.5", + "@babel/plugin-transform-block-scoped-functions": "^7.14.5", + "@babel/plugin-transform-block-scoping": "^7.14.5", + "@babel/plugin-transform-classes": "^7.14.5", + "@babel/plugin-transform-computed-properties": "^7.14.5", + "@babel/plugin-transform-destructuring": "^7.14.7", + "@babel/plugin-transform-dotall-regex": "^7.14.5", + "@babel/plugin-transform-duplicate-keys": "^7.14.5", + "@babel/plugin-transform-exponentiation-operator": "^7.14.5", + "@babel/plugin-transform-for-of": "^7.14.5", + "@babel/plugin-transform-function-name": "^7.14.5", + "@babel/plugin-transform-literals": "^7.14.5", + "@babel/plugin-transform-member-expression-literals": "^7.14.5", + "@babel/plugin-transform-modules-amd": "^7.14.5", + "@babel/plugin-transform-modules-commonjs": "^7.14.5", + "@babel/plugin-transform-modules-systemjs": "^7.14.5", + "@babel/plugin-transform-modules-umd": "^7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.7", + "@babel/plugin-transform-new-target": "^7.14.5", + "@babel/plugin-transform-object-super": "^7.14.5", + "@babel/plugin-transform-parameters": "^7.14.5", + "@babel/plugin-transform-property-literals": "^7.14.5", + "@babel/plugin-transform-regenerator": "^7.14.5", + "@babel/plugin-transform-reserved-words": "^7.14.5", + "@babel/plugin-transform-shorthand-properties": "^7.14.5", + "@babel/plugin-transform-spread": "^7.14.6", + "@babel/plugin-transform-sticky-regex": "^7.14.5", + "@babel/plugin-transform-template-literals": "^7.14.5", + "@babel/plugin-transform-typeof-symbol": "^7.14.5", + "@babel/plugin-transform-unicode-escapes": "^7.14.5", + "@babel/plugin-transform-unicode-regex": "^7.14.5", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.14.8", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.2", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "core-js-compat": "^3.15.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", + "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@bitwarden/jslib-angular": { + "resolved": "angular", + "link": true + }, + "node_modules/@bitwarden/jslib-common": { + "resolved": "common", + "link": true + }, + "node_modules/@bitwarden/jslib-electron": { + "resolved": "electron", + "link": true + }, + "node_modules/@bitwarden/jslib-node": { + "resolved": "node", + "link": true + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", + "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@electron/get": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=8.6" + }, + "optionalDependencies": { + "global-agent": "^3.0.0", + "global-tunnel-ng": "^2.7.1" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@fluffy-spoon/substitute": { + "version": "1.208.0", + "resolved": "https://registry.npmjs.org/@fluffy-spoon/substitute/-/substitute-1.208.0.tgz", + "integrity": "sha512-BU5vKRoK4OYlKzDtyg4HbtWnUNLOvV0ntqEZIphz+mq2G0HlVFywwJ7M+FbIcnJVDbUReS01FyL5x8R01r7zBg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/substitute-js#section-contribute" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@jest/reporters/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-1.0.0.tgz", + "integrity": "sha512-9oLAnygRMi8Q5QkYEU4XWK04B+nuoXoxjRvRxgjuChkLZFBja0YPSgdZ7dZtwhncLBcQe/I/E+fLuk5qxcYVJA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jsdevtools/coverage-istanbul-loader": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", + "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", + "dev": true, + "dependencies": { + "convert-source-map": "^1.7.0", + "istanbul-lib-instrument": "^4.0.3", + "loader-utils": "^2.0.0", + "merge-source-map": "^1.1.0", + "schema-utils": "^2.7.0" + } + }, + "node_modules/@microsoft/signalr": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz", + "integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.7.3", + "node-fetch": "^2.6.0", + "ws": "^6.0.0" + } + }, + "node_modules/@microsoft/signalr-protocol-msgpack": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz", + "integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==", + "dependencies": { + "@microsoft/signalr": ">=5.0.10", + "msgpack5": "^4.5.0" + } + }, + "node_modules/@microsoft/signalr/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/@ngtools/webpack": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.17.tgz", + "integrity": "sha512-uaS+2YZgPDW3VmUuwh4/yfIFV1KRVGWefc6xLWIqKRKs6mlRYs65m3ib9dX7CTS4kQMCbhxkxMbpBO2yXlzfvA==", + "dev": true, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^12.0.0", + "typescript": "~4.2.3 || ~4.3.2", + "webpack": "^5.30.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/duo_web_sdk": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz", + "integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "node_modules/@types/inquirer/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@types/inquirer/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "27.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", + "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "dev": true, + "dependencies": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "node_modules/@types/lodash": { + "version": "4.14.181", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.181.tgz", + "integrity": "sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==", + "dev": true + }, + "node_modules/@types/lowdb": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/lowdb/-/lowdb-1.0.11.tgz", + "integrity": "sha512-h99VMxvTuz+VsXUVCCJo4dsps4vbkXwvU71TpmxDoiBU24bJ0VBygIHgmMm+UPoQIFihmV6euRik4z8J7XDJWg==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/lunr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", + "integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/node-forge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", + "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==" + }, + "node_modules/@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==", + "dev": true + }, + "node_modules/@types/webpack-sources": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.9.tgz", + "integrity": "sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + } + }, + "node_modules/@types/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@types/zxcvbn": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz", + "integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true, + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.0.tgz", + "integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/autoprefixer/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/autoprefixer/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/autoprefixer/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dev": true, + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-loader": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", + "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz", + "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.4", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz", + "integrity": "sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.2.2", + "core-js-compat": "^3.16.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz", + "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.2.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "node_modules/big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "optional": true + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-hrtime": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", + "integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA==" + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/builder-util-runtime": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.0.0.tgz", + "integrity": "sha512-SkpEtSmTkREDHRJnxKEv43aAYp8sYWY8fxYBhGLBLOBIRXeaIp6Kv3lBgSD7uR8jQtC7CA659sqJrpSV6zNvSA==", + "dependencies": { + "debug": "^4.3.2", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz", + "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==", + "dev": true, + "dependencies": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001325", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz", + "integrity": "sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "dev": true + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "bin/concurrently.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/concurrently/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/concurrently/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conf": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.1.2.tgz", + "integrity": "sha512-o9Fv1Mv+6A0JpoayQ8JleNp3hhkbOJP/Re/Q+QqxMPHPkABVsRjQGWZn9A5GcqLiTNC6d89p2PB5ZhHVDSMwyg==", + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/conf/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/conf/node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz", + "integrity": "sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.5", + "glob-parent": "^6.0.0", + "globby": "^11.0.3", + "normalize-path": "^3.0.0", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz", + "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "dev": true, + "dependencies": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/critters": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz", + "integrity": "sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.1.3", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/node_modules/postcss": { + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.1", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/critters/node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "node_modules/css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5" + }, + "bin": { + "css-blank-pseudo": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-blank-pseudo/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/css-blank-pseudo/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-blank-pseudo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", + "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "bin": { + "css-has-pseudo": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-has-pseudo/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-has-pseudo/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/css-has-pseudo/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-has-pseudo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.2.0.tgz", + "integrity": "sha512-/rvHfYRjIpymZblf49w8jYcRo2y9gj6rV8UroHGmBxKrIyGLokpycyKzp9OkitvqT29ZSpzJ0Ic7SpnJX3sC8g==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.2.tgz", + "integrity": "sha512-B3I5e17RwvKPJwsxjjWcdgpU/zqylzK1bPVghcmpFHRL48DXiBgrtqz1BJsn68+t/zzaLp9kYAaEDvQ7GyanFQ==", + "dev": true, + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "p-limit": "^3.0.2", + "postcss": "^8.3.5", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", + "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", + "dev": true, + "dependencies": { + "css": "^2.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5" + }, + "bin": { + "css-prefers-color-scheme": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-prefers-color-scheme/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/css-prefers-color-scheme/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-prefers-color-scheme/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==", + "dev": true + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.7.tgz", + "integrity": "sha512-pVsUV6LcTXif7lvKKW9ZrmX+rGRzxkEdJuVJcp5ftUjWITgwam5LMZOgaTvUrWPkcORBey6he7JKb4XAJvrpKg==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^5.2.7", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz", + "integrity": "sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^6.2.2", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.0", + "postcss-discard-comments": "^5.1.1", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.4", + "postcss-merge-rules": "^5.1.1", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.2", + "postcss-minify-selectors": "^5.2.0", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.0", + "postcss-normalize-repeat-style": "^5.1.0", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.1", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "dev": true, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dependencies": { + "mimic-fn": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debounce-fn/node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "dependencies": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/default-gateway/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/default-gateway/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/default-gateway/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/default-gateway/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/default-gateway/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/default-gateway/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/default-gateway/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "devOptional": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "devOptional": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duo_web_sdk": { + "version": "2.7.0", + "resolved": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/electron": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-16.1.0.tgz", + "integrity": "sha512-G4fpHmE3sgd497e0zEier/AmZ4fyReX8ozYAl468+FaI5kb44+69igRHQwRUtmPzv+fCn/Jm4wJQPfLe60WmUQ==", + "hasInstallScript": true, + "dependencies": { + "@electron/get": "^1.13.0", + "@types/node": "^14.6.2", + "extract-zip": "^1.0.3" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 8.6" + } + }, + "node_modules/electron-log": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-4.4.6.tgz", + "integrity": "sha512-nirYgRdY+F+vclr8ijdwy2vW03IzFpDHTaKNWu76dEN21Y76+smcES5knS7cgHUUB0qNLOi8vZO36taakjbSXA==" + }, + "node_modules/electron-store": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.1.tgz", + "integrity": "sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==", + "dependencies": { + "conf": "^10.0.3", + "type-fest": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-store/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", + "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", + "dev": true + }, + "node_modules/electron-updater": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.0.0.tgz", + "integrity": "sha512-SC3sw92ewjrJFZIJamVOHqxW3yzFin/Q/Swf2FZodqm9xd4s8hCbPCfptpD/xBIcvQmAv2BAggbprwWq/fyp6w==", + "dependencies": { + "@types/semver": "^7.3.6", + "builder-util-runtime": "9.0.0", + "fs-extra": "^10.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.3.5" + } + }, + "node_modules/electron-updater/node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-updater/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-updater/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron/node_modules/@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "devOptional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", + "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true, + "peer": true + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-denodeify": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz", + "integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8=" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "node_modules/esbuild": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.8.tgz", + "integrity": "sha512-A4af7G7YZLfG5OnARJRMtlpEsCkq/zHZQXewgPA864l9D6VjjbH1SuFYK/OSV6BtHwDGkdwyRrX0qQFLnMfUcw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.13.8", + "esbuild-darwin-64": "0.13.8", + "esbuild-darwin-arm64": "0.13.8", + "esbuild-freebsd-64": "0.13.8", + "esbuild-freebsd-arm64": "0.13.8", + "esbuild-linux-32": "0.13.8", + "esbuild-linux-64": "0.13.8", + "esbuild-linux-arm": "0.13.8", + "esbuild-linux-arm64": "0.13.8", + "esbuild-linux-mips64le": "0.13.8", + "esbuild-linux-ppc64le": "0.13.8", + "esbuild-netbsd-64": "0.13.8", + "esbuild-openbsd-64": "0.13.8", + "esbuild-sunos-64": "0.13.8", + "esbuild-windows-32": "0.13.8", + "esbuild-windows-64": "0.13.8", + "esbuild-windows-arm64": "0.13.8" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz", + "integrity": "sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/esbuild-darwin-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz", + "integrity": "sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz", + "integrity": "sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz", + "integrity": "sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz", + "integrity": "sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-linux-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz", + "integrity": "sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz", + "integrity": "sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz", + "integrity": "sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz", + "integrity": "sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz", + "integrity": "sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz", + "integrity": "sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.11.tgz", + "integrity": "sha512-DoThrkzunZ1nfRGoDN6REwmo8ZZWHd2ztniPVIR5RMw/Il9wiWEYBahb8jnMzQaSOxBsGp0PbyJeVLTUatnlcw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz", + "integrity": "sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz", + "integrity": "sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/esbuild-sunos-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz", + "integrity": "sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/esbuild-wasm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.13.8.tgz", + "integrity": "sha512-UbD+3nloiSpJWXTCInZQrqPe8Y+RLfDkY/5kEHiXsw/lmaEvibe69qTzQu16m5R9je/0bF7VYQ5jaEOq0z9lLA==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz", + "integrity": "sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz", + "integrity": "sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz", + "integrity": "sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "devOptional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "dependencies": { + "original": "^1.0.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dependencies": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/extract-zip/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/extract-zip/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-cookie": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz", + "integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==", + "dependencies": { + "es6-denodeify": "^0.1.1", + "tough-cookie": "^2.3.3" + } + }, + "node_modules/fetch-cookie/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "node_modules/flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "dependencies": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "optional": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/got/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "dependencies": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/inquirer": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", + "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.3.0", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "dependencies": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd/node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dev": true, + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-preset-angular": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-11.1.1.tgz", + "integrity": "sha512-ZlYiKJhAQSU9wIjncX59xutcj49R4MiDsTPSwZiwdTAHQvHm32MS6SGimQIVBqh1DukfwYX0NXKS0D/onLAsLQ==", + "dev": true, + "dependencies": { + "bs-logger": "^0.2.6", + "esbuild-wasm": "0.14.11", + "jest-environment-jsdom": "^27.0.0", + "pretty-format": "^27.0.0", + "ts-jest": "^27.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "optionalDependencies": { + "esbuild": "0.14.11" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">=0.1002.4", + "@angular/compiler-cli": ">=10.0.0", + "@angular/core": ">=10.0.0", + "@angular/platform-browser-dynamic": ">=10.0.0" + } + }, + "node_modules/jest-preset-angular/node_modules/esbuild": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.11.tgz", + "integrity": "sha512-xZvPtVj6yecnDeFb3KjjCM6i7B5TCAQZT77kkW/CpXTMnd6VLnRPKrUB1XHI1pSq6a4Zcy3BGueQ8VljqjDGCg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.14.11", + "esbuild-darwin-64": "0.14.11", + "esbuild-darwin-arm64": "0.14.11", + "esbuild-freebsd-64": "0.14.11", + "esbuild-freebsd-arm64": "0.14.11", + "esbuild-linux-32": "0.14.11", + "esbuild-linux-64": "0.14.11", + "esbuild-linux-arm": "0.14.11", + "esbuild-linux-arm64": "0.14.11", + "esbuild-linux-mips64le": "0.14.11", + "esbuild-linux-ppc64le": "0.14.11", + "esbuild-linux-s390x": "0.14.11", + "esbuild-netbsd-64": "0.14.11", + "esbuild-openbsd-64": "0.14.11", + "esbuild-sunos-64": "0.14.11", + "esbuild-windows-32": "0.14.11", + "esbuild-windows-64": "0.14.11", + "esbuild-windows-arm64": "0.14.11" + } + }, + "node_modules/jest-preset-angular/node_modules/esbuild-android-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.11.tgz", + "integrity": "sha512-6iHjgvMnC/SzDH8TefL+/3lgCjYWwAd1LixYfmz/TBPbDQlxcuSkX0yiQgcJB9k+ibZ54yjVXziIwGdlc+6WNw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-darwin-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.11.tgz", + "integrity": "sha512-olq84ikh6TiBcrs3FnM4eR5VPPlcJcdW8BnUz/lNoEWYifYQ+Po5DuYV1oz1CTFMw4k6bQIZl8T3yxL+ZT2uvQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-darwin-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.11.tgz", + "integrity": "sha512-Jj0ieWLREPBYr/TZJrb2GFH8PVzDqiQWavo1pOFFShrcmHWDBDrlDxPzEZ67NF/Un3t6sNNmeI1TUS/fe1xARg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-freebsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.11.tgz", + "integrity": "sha512-C5sT3/XIztxxz/zwDjPRHyzj/NJFOnakAanXuyfLDwhwupKPd76/PPHHyJx6Po6NI6PomgVp/zi6GRB8PfrOTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-freebsd-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.11.tgz", + "integrity": "sha512-y3Llu4wbs0bk4cwjsdAtVOesXb6JkdfZDLKMt+v1U3tOEPBdSu6w8796VTksJgPfqvpX22JmPLClls0h5p+L9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-32": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.11.tgz", + "integrity": "sha512-Cg3nVsxArjyLke9EuwictFF3Sva+UlDTwHIuIyx8qpxRYAOUTmxr2LzYrhHyTcGOleLGXUXYsnUVwKqnKAgkcg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.11.tgz", + "integrity": "sha512-oeR6dIrrojr8DKVrxtH3xl4eencmjsgI6kPkDCRIIFwv4p+K7ySviM85K66BN01oLjzthpUMvBVfWSJkBLeRbg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-arm": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.11.tgz", + "integrity": "sha512-vcwskfD9g0tojux/ZaTJptJQU3a7YgTYsptK1y6LQ/rJmw7U5QJvboNawqM98Ca3ToYEucfCRGbl66OTNtp6KQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.11.tgz", + "integrity": "sha512-+e6ZCgTFQYZlmg2OqLkg1jHLYtkNDksxWDBWNtI4XG4WxuOCUErLqfEt9qWjvzK3XBcCzHImrajkUjO+rRkbMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-mips64le": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.11.tgz", + "integrity": "sha512-Rrs99L+p54vepmXIb87xTG6ukrQv+CzrM8eoeR+r/OFL2Rg8RlyEtCeshXJ2+Q66MXZOgPJaokXJZb9snq28bw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-ppc64le": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.11.tgz", + "integrity": "sha512-JyzziGAI0D30Vyzt0HDihp4s1IUtJ3ssV2zx9O/c+U/dhUHVP2TmlYjzCfCr2Q6mwXTeloDcLS4qkyvJtYptdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-netbsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.11.tgz", + "integrity": "sha512-12luoRQz+6eihKYh1zjrw0CBa2aw3twIiHV/FAfjh2NEBDgJQOY4WCEUEN+Rgon7xmLh4XUxCQjnwrvf8zhACw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-openbsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.11.tgz", + "integrity": "sha512-l18TZDjmvwW6cDeR4fmizNoxndyDHamGOOAenwI4SOJbzlJmwfr0jUgjbaXCUuYVOA964siw+Ix+A+bhALWg8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-sunos-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.11.tgz", + "integrity": "sha512-bmYzDtwASBB8c+0/HVOAiE9diR7+8zLm/i3kEojUH2z0aIs6x/S4KiTuT5/0VKJ4zk69kXel1cNWlHBMkmavQg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-wasm": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.11.tgz", + "integrity": "sha512-9e1R6hv0hiU+BkJI2edqUuWfXUbOP2Mox+Ijl/uY1vLLlSsunkrcADqD/4Rz+VCEDzw6ecscJM+uJqR2fRmEUg==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-preset-angular/node_modules/esbuild-windows-32": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.11.tgz", + "integrity": "sha512-J1Ys5hMid8QgdY00OBvIolXgCQn1ARhYtxPnG6ESWNTty3ashtc4+As5nTrsErnv8ZGUcWZe4WzTP/DmEVX1UQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-windows-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.11.tgz", + "integrity": "sha512-h9FmMskMuGeN/9G9+LlHPAoiQk9jlKDUn9yA0MpiGzwLa82E7r1b1u+h2a+InprbSnSLxDq/7p5YGtYVO85Mlg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-windows-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.11.tgz", + "integrity": "sha512-dZp7Krv13KpwKklt9/1vBFBMqxEQIO6ri7Azf8C+ob4zOegpJmha2XY9VVWP/OyQ0OWk6cEeIzMJwInRZrzBUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" + }, + "node_modules/less": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz", + "integrity": "sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^1.10.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.0.1.tgz", + "integrity": "sha512-Crln//HpW9M5CbtdfWm3IO66Cvx1WhZQvNybXgfB2dD/6Sav9ppw+IWqs/FQKPBFO4B6X0X28Z0WNznshgwUzA==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/less/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "2.3.20", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz", + "integrity": "sha512-AHVueg9clOKACSHkhmEI+PCC9x8+qsQVuKECZD3ETxETK5h/PCv5/MUzyG1gm8OMcip/s1tcNxqo9Qb7WhjGsg==", + "dev": true, + "dependencies": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "12.3.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.3.7.tgz", + "integrity": "sha512-/S4D726e2GIsDVWIk1XGvheCaDm1SJRQp8efamZFWJxQMVEbOwSysp7xb49Oo73KYCdy97mIWinhlxcoNqIfIQ==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.16", + "commander": "^8.3.0", + "debug": "^4.3.3", + "execa": "^5.1.1", + "lilconfig": "2.0.4", + "listr2": "^4.0.1", + "micromatch": "^4.0.4", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.0", + "pidtree": "^0.5.0", + "string-argv": "^0.3.1", + "supports-color": "^9.2.1", + "yaml": "^1.10.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/lint-staged/node_modules/lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lint-staged/node_modules/supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/lowdb": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "dependencies": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "node_modules/magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", + "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", + "dev": true, + "dependencies": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/mem?sponsor=1" + } + }, + "node_modules/mem/node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/memory-fs/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/memory-fs/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/merge-source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.2.tgz", + "integrity": "sha512-ZmqShkn79D36uerdED+9qdo1ZYG8C1YsWvXu0UMJxurZnSdgz7gQKO2EGv8T55MhDqG3DYmGtizZNpM/UbTlcA==", + "dev": true, + "dependencies": { + "schema-utils": "^3.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/msgpack5": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", + "integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==", + "dependencies": { + "bl": "^2.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.3.6", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/msgpack5/node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/msgpack5/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/msgpack5/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", + "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "dev": true, + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true + }, + "node_modules/nodemon": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", + "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5", + "update-notifier": "^5.1.0" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "dependencies": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "devOptional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", + "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opn/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dependencies": { + "url-parse": "^1.4.3" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "dependencies": { + "retry": "^0.12.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/piscina": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.1.0.tgz", + "integrity": "sha512-KTW4sjsCD34MHrUbx9eAAbuUSpVj407hQSgk/6Epkg0pbRBmv4a3UX7Sr8wxm9xYqQLnsN4mFOjqGDzHAdgKQg==", + "dev": true, + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", + "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", + "dev": true, + "dependencies": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + } + }, + "node_modules/postcss-attribute-case-insensitive/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-attribute-case-insensitive/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-attribute-case-insensitive/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-functional-notation/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-functional-notation/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-functional-notation/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "dev": true, + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-gray/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-gray/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-gray/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-hex-alpha/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-hex-alpha/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-hex-alpha/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "dev": true, + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-mod-function/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-mod-function/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-mod-function/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-rebeccapurple/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-rebeccapurple/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-rebeccapurple/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz", + "integrity": "sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-media/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-custom-media/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-custom-media/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-properties/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-custom-properties/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-custom-properties/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-selectors/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-custom-selectors/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-custom-selectors/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-custom-selectors/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-dir-pseudo-class/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz", + "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-double-position-gradients/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-double-position-gradients/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-double-position-gradients/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-env-function/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-env-function/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-env-function/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-focus-visible/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-focus-visible/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-focus-visible/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-focus-within/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-focus-within/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-focus-within/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-font-variant": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz", + "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-font-variant/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-font-variant/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-font-variant/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-gap-properties/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-gap-properties/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-gap-properties/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-image-set-function/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-image-set-function/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-image-set-function/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-import": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", + "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", + "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-initial/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-initial/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-initial/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "dev": true, + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-lab-function/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-lab-function/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-lab-function/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-loader": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.1.1.tgz", + "integrity": "sha512-lBmJMvRh1D40dqpWKr9Rpygwxn8M74U9uaCSeYGNKLGInbk9mXBt1ultHf2dH9Ghk6Ue4UXlXWwGMH9QdUJ5ug==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-logical/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-logical/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-logical/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-media-minmax/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-media-minmax/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-media-minmax/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", + "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz", + "integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz", + "integrity": "sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz", + "integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-nesting/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-nesting/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz", + "integrity": "sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz", + "integrity": "sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz", + "integrity": "sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==", + "dev": true, + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-overflow-shorthand/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-overflow-shorthand/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-overflow-shorthand/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-page-break/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-page-break/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-page-break/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-place/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-place/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-place/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "dev": true, + "dependencies": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-preset-env/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-preset-env/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-preset-env/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-replace-overflow-wrap/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-replace-overflow-wrap/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-replace-overflow-wrap/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-selector-matches/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-selector-matches/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-selector-matches/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-selector-not": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz", + "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-selector-not/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-selector-not/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-selector-not/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "dev": true, + "dependencies": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=6.14.4" + } + }, + "node_modules/postcss/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "node_modules/postinstall-build": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.3.tgz", + "integrity": "sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==", + "deprecated": "postinstall-build's behavior is now built into npm! You should migrate off of postinstall-build and use the new `prepare` lifecycle script with npm 5.0.0 or greater.", + "dev": true, + "bin": { + "postinstall-build": "cli.js" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=8.9" + }, + "peerDependencies": { + "rework": "1.0.1", + "rework-visit": "1.0.0" + }, + "peerDependenciesMeta": { + "rework": { + "optional": true + }, + "rework-visit": { + "optional": true + } + } + }, + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/roarr/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.36.0.tgz", + "integrity": "sha512-fQzEjipfOv5kh930nu3Imzq3ie/sGDc/4KtQMJlt7RRdrkQSfe37Bwi/Rf/gfuYHsIuE1fIlDMvpyMcEwjnPvg==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz", + "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0", + "sass": "^1.3.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "node_modules/selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/selfsigned/node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs-client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz", + "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^1.1.0", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.0.tgz", + "integrity": "sha512-GKGWqWvYr04M7tn8dryIWvb0s8YM41z82iQv01yBtIylgxax0CwvSy6gc2Y02iuXwEfGWRlMicH0nvms9UZphw==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "node_modules/spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/steno": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "dependencies": { + "graceful-fs": "^4.1.3" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.2.1.tgz", + "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/stylus": { + "version": "0.54.8", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", + "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", + "dev": true, + "dependencies": { + "css-parse": "~2.0.0", + "debug": "~3.1.0", + "glob": "^7.1.6", + "mkdirp": "~1.0.4", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "semver": "^6.3.0", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stylus-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.1.0.tgz", + "integrity": "sha512-qKO34QCsOtSJrXxQQmXsPeaVHh6hMumBAFIoJTcsSr2VzrA6o/CW9HCGR8spCjzJhN8oKQHdj/Ytx0wwXyElkw==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.5", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/stylus/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/stylus/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/stylus/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", + "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz", + "integrity": "sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==", + "dev": true, + "dependencies": { + "jest-worker": "^27.0.2", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "hasInstallScript": true, + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/tldjs/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-jest": { + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@types/jest": "^27.0.0", + "babel-jest": ">=27.0.0 <28", + "jest": "^27.0.0", + "typescript": ">=3.8 <5.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.3.0.tgz", + "integrity": "sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "*" + } + }, + "node_modules/ts-node": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/ttypescript": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", + "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", + "dev": true, + "dependencies": { + "resolve": ">=1.9.0" + }, + "bin": { + "ttsc": "bin/tsc", + "ttsserver": "bin/tsserver" + }, + "peerDependencies": { + "ts-node": ">=8.0.2", + "typescript": ">=3.2.2" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typemoq": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typemoq/-/typemoq-2.1.0.tgz", + "integrity": "sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "circular-json": "^0.3.1", + "lodash": "^4.17.4", + "postinstall-build": "^5.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typescript-transform-paths": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/typescript-transform-paths/-/typescript-transform-paths-2.2.4.tgz", + "integrity": "sha512-i+/sgp3rw1ZronMCm2TKGBy1dlvN88Kd8CCb+HWnOE8+Hv0uIVnbC8xM5AD2t1JBCWabEhuH9p3n8DOVi0+R6g==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "peerDependencies": { + "typescript": ">=3.6.5" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "dependencies": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.71.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.71.0.tgz", + "integrity": "sha512-g4dFT7CFG8LY0iU5G8nBL6VlkT21Z7dcYDpJAEJV5Q1WLb9UwnFbrem1k7K52ILqEmomN7pnzWFxxE6SlDY56A==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.2", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.0.0.tgz", + "integrity": "sha512-9zng2Z60pm6A98YoRcA0wSxw1EYn7B7y5owX/Tckyt9KGyULTkLtiavjaXlWqOMkM0YtqGgL3PvMOFgyFLq8vw==", + "dev": true, + "dependencies": { + "colorette": "^1.2.2", + "mem": "^8.1.1", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack-dev-middleware/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", + "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==", + "dev": true, + "dependencies": { + "ansi-html-community": "0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 6.11.5" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/webpack-dev-server/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "dependencies": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/webpack-dev-server/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/webpack-dev-server/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack-dev-server/node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack-dev-server/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/webpack-dev-server/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dev": true, + "dependencies": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/webpack-dev-server/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "dependencies": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/webpack-log/node_modules/ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-log/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.2.tgz", + "integrity": "sha512-GBWYBoyalbo5YClwWop9qe6Zclp8CIXYGIz12OPclJhIrSplDxs1Ls1JDMH8xBPPrg1T6ISaTW9Y6zOrwEiAzw==", + "dev": true, + "dependencies": { + "webpack-sources": "^1.3.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 2.21.0 < 5", + "webpack": ">= 1.12.11 < 6" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + }, + "node/node_modules/inquirer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.0.1.tgz", + "integrity": "sha512-Ta9bMA3EtUHDaZJXqUoT5cn/EecwOp+SXpKJqxDbDuMbLvEMu6YTyDDuvTWeStODfdmXyfMo7LymQyPkN3BicA==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "1.0.0", + "sourcemap-codec": "1.4.8" + } + }, + "@angular-devkit/architect": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.17.tgz", + "integrity": "sha512-uUQcHcLbPvr9adALQSLU1MTDduVUR2kZAHi2e7SmL9ioel84pPVXBoD0WpSBeUMKwPiDs3TQDaxDB49hl0nBSQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "12.2.17", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/build-angular": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.17.tgz", + "integrity": "sha512-uc3HGHVQyatqQ/M53oxYBvhz0jx0hgdc7WT+L56GLHvgz7Ct2VEbpWaMfwHkFfE1F1iHkIgnTKHKWacJl1yQIg==", + "dev": true, + "requires": { + "@ampproject/remapping": "1.0.1", + "@angular-devkit/architect": "0.1202.17", + "@angular-devkit/build-optimizer": "0.1202.17", + "@angular-devkit/build-webpack": "0.1202.17", + "@angular-devkit/core": "12.2.17", + "@babel/core": "7.14.8", + "@babel/generator": "7.14.8", + "@babel/helper-annotate-as-pure": "7.14.5", + "@babel/plugin-proposal-async-generator-functions": "7.14.7", + "@babel/plugin-transform-async-to-generator": "7.14.5", + "@babel/plugin-transform-runtime": "7.14.5", + "@babel/preset-env": "7.14.8", + "@babel/runtime": "7.14.8", + "@babel/template": "7.14.5", + "@discoveryjs/json-ext": "0.5.3", + "@jsdevtools/coverage-istanbul-loader": "3.0.5", + "@ngtools/webpack": "12.2.17", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.2", + "browserslist": "^4.9.1", + "cacache": "15.2.0", + "caniuse-lite": "^1.0.30001032", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "9.0.1", + "core-js": "3.16.0", + "critters": "0.0.12", + "css-loader": "6.2.0", + "css-minimizer-webpack-plugin": "3.0.2", + "esbuild": "0.13.8", + "esbuild-wasm": "0.13.8", + "find-cache-dir": "3.3.1", + "glob": "7.1.7", + "https-proxy-agent": "5.0.0", + "inquirer": "8.1.2", + "karma-source-map-support": "1.4.0", + "less": "4.1.1", + "less-loader": "10.0.1", + "license-webpack-plugin": "2.3.20", + "loader-utils": "2.0.0", + "mini-css-extract-plugin": "2.4.2", + "minimatch": "3.0.4", + "open": "8.2.1", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.1.0", + "postcss": "8.3.6", + "postcss-import": "14.0.2", + "postcss-loader": "6.1.1", + "postcss-preset-env": "6.7.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "4.0.0", + "rxjs": "6.6.7", + "sass": "1.36.0", + "sass-loader": "12.1.0", + "semver": "7.3.5", + "source-map-loader": "3.0.0", + "source-map-support": "0.5.19", + "style-loader": "3.2.1", + "stylus": "0.54.8", + "stylus-loader": "6.1.0", + "terser": "5.7.1", + "terser-webpack-plugin": "5.1.4", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.0", + "webpack": "5.50.0", + "webpack-dev-middleware": "5.0.0", + "webpack-dev-server": "3.11.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "1.5.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "es-module-lexer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz", + "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "webpack": { + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.50.0.tgz", + "integrity": "sha512-hqxI7t/KVygs0WRv/kTgUW8Kl3YC81uyWQSo/7WUs5LsuRw0htH/fCwbVBGCuiX/t4s7qzjXFcf41O8Reiypag==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.0", + "es-module-lexer": "^0.7.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.2.0", + "webpack-sources": "^3.2.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + } + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.17.tgz", + "integrity": "sha512-1qWGWw7cCNADB4LZ/zjiSK0GLmr2kebYyNG0KutCE8GNVxv2h6w6dJP6t1C/BgskRuBPCAhvE+lEKN8ljSutag==", + "dev": true, + "requires": { + "source-map": "0.7.3", + "tslib": "2.3.0", + "typescript": "4.3.5" + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.17.tgz", + "integrity": "sha512-z7FW43DJ4p8UZwbFRmMrh2ohqhI2Wtdg3+FZiTnl4opb3zYheGiNxPlTuiyKjG21JUkGCdthkkBLCNfaUU0U/Q==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1202.17", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/core": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.17.tgz", + "integrity": "sha512-PyOY7LGUPPd6rakxUYbfQN6zAdOCMCouVp5tERY1WTdMdEiuULOtHsPee8kNbh75pD59KbJNU+fwozPRMuIm5g==", + "dev": true, + "requires": { + "ajv": "8.6.2", + "ajv-formats": "2.1.0", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular/animations": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.16.tgz", + "integrity": "sha512-Kf6C7Ta+fCMq5DvT9JNVhBkcECrqFa3wumiC6ssGo5sNaEzXz+tlep9ZgEbqfxSn7gAN7L1DgsbS9u0O6tbUkg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/cdk": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz", + "integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + } + } + }, + "@angular/common": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.16.tgz", + "integrity": "sha512-FEqTXTEsnbDInqV1yFlm97Tz1OFqZS5t0TUkm8gzXRgpIce/F/jLwAg0u1VQkgOsno6cNm0xTWPoZgu85NI4ug==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/compiler": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.16.tgz", + "integrity": "sha512-nsYEw+yu8QyeqPf9nAmG419i1mtGM4v8+U+S3eQHQFXTgJzLymMykWHYu2ETdjUpNSLK6xcIQDBWtWnWSfJjAA==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/compiler-cli": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.16.tgz", + "integrity": "sha512-tlalh8SJvdCWbUPRUR5GamaP+wSc/GuCsoUZpSbcczGKgSlbaEVXUYtVXm8/wuT6Slk2sSEbRs7tXGF2i7qxVw==", + "dev": true, + "requires": { + "@babel/core": "^7.8.6", + "@babel/types": "^7.8.6", + "canonical-path": "1.0.0", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "source-map": "^0.6.1", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.2.0", + "yargs": "^17.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@angular/core": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.16.tgz", + "integrity": "sha512-jsmvaRdAfng99z2a9mAmkfcsCE1wm+tBYVDxnc5JquSXznwtncjzcoc2X0J0dzrkCDvzFfpTsZ9vehylytBc+A==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/forms": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.16.tgz", + "integrity": "sha512-sb+gpNun5aN7CZfHXS6X7vJcd/0A1P/gRBZpYtQTzBYnqEFCOFIvR62eb05aHQ4JhgKaSPpIXrbz/bAwY/njZw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/platform-browser": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.16.tgz", + "integrity": "sha512-T855ppLeQO6hRHi7lGf5fwPoUVt+c0h2rgkV5jHElc3ylaGnhecmZc6fnWLX4pw82TMJUgUV88CY8JCFabJWwg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.16.tgz", + "integrity": "sha512-XGxoACAMW/bc3atiVRpaiYwU4LkobYwVzwlxTT/BxOfsdt8ILb5wU8Fx1TMKNECOQHSGdK0qqhch4pTBZ3cb2g==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/router": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.16.tgz", + "integrity": "sha512-LuFXSMIvX/VrB4jbYhigG2Y2pGQ9ULsSBUwDWwQCf4kr0eVI37LBJ2Vr74GBEznjgQ0UmWE89E+XYI80UhERTw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true + }, + "@babel/core": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", + "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.8", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.8", + "@babel/helpers": "^7.14.8", + "@babel/parser": "^7.14.8", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", + "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", + "dev": true, + "requires": { + "@babel/types": "^7.14.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz", + "integrity": "sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", + "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.4.tgz", + "integrity": "sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "dependencies": { + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "dependencies": { + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "dependencies": { + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helpers": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "dev": true, + "requires": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "dependencies": { + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "dev": true + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz", + "integrity": "sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", + "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz", + "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz", + "integrity": "sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.2", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/preset-env": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.8.tgz", + "integrity": "sha512-a9aOppDU93oArQ51H+B8M1vH+tayZbuBqzjOhntGetZVa+4tTu5jp+XTwqHGG2lxslqomPYVSjIxQkFwXzgnxg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.14.7", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-async-generator-functions": "^7.14.7", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-class-static-block": "^7.14.5", + "@babel/plugin-proposal-dynamic-import": "^7.14.5", + "@babel/plugin-proposal-export-namespace-from": "^7.14.5", + "@babel/plugin-proposal-json-strings": "^7.14.5", + "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", + "@babel/plugin-proposal-numeric-separator": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.14.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", + "@babel/plugin-proposal-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-private-methods": "^7.14.5", + "@babel/plugin-proposal-private-property-in-object": "^7.14.5", + "@babel/plugin-proposal-unicode-property-regex": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.14.5", + "@babel/plugin-transform-async-to-generator": "^7.14.5", + "@babel/plugin-transform-block-scoped-functions": "^7.14.5", + "@babel/plugin-transform-block-scoping": "^7.14.5", + "@babel/plugin-transform-classes": "^7.14.5", + "@babel/plugin-transform-computed-properties": "^7.14.5", + "@babel/plugin-transform-destructuring": "^7.14.7", + "@babel/plugin-transform-dotall-regex": "^7.14.5", + "@babel/plugin-transform-duplicate-keys": "^7.14.5", + "@babel/plugin-transform-exponentiation-operator": "^7.14.5", + "@babel/plugin-transform-for-of": "^7.14.5", + "@babel/plugin-transform-function-name": "^7.14.5", + "@babel/plugin-transform-literals": "^7.14.5", + "@babel/plugin-transform-member-expression-literals": "^7.14.5", + "@babel/plugin-transform-modules-amd": "^7.14.5", + "@babel/plugin-transform-modules-commonjs": "^7.14.5", + "@babel/plugin-transform-modules-systemjs": "^7.14.5", + "@babel/plugin-transform-modules-umd": "^7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.7", + "@babel/plugin-transform-new-target": "^7.14.5", + "@babel/plugin-transform-object-super": "^7.14.5", + "@babel/plugin-transform-parameters": "^7.14.5", + "@babel/plugin-transform-property-literals": "^7.14.5", + "@babel/plugin-transform-regenerator": "^7.14.5", + "@babel/plugin-transform-reserved-words": "^7.14.5", + "@babel/plugin-transform-shorthand-properties": "^7.14.5", + "@babel/plugin-transform-spread": "^7.14.6", + "@babel/plugin-transform-sticky-regex": "^7.14.5", + "@babel/plugin-transform-template-literals": "^7.14.5", + "@babel/plugin-transform-typeof-symbol": "^7.14.5", + "@babel/plugin-transform-unicode-escapes": "^7.14.5", + "@babel/plugin-transform-unicode-regex": "^7.14.5", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.14.8", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.2", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "core-js-compat": "^3.15.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", + "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@bitwarden/jslib-angular": { + "version": "file:angular", + "requires": { + "@angular/animations": "^12.2.13", + "@angular/cdk": "^12.2.13", + "@angular/common": "^12.2.13", + "@angular/compiler": "^12.2.13", + "@angular/core": "^12.2.13", + "@angular/forms": "^12.2.13", + "@angular/platform-browser": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@angular/router": "^12.2.13", + "@bitwarden/jslib-common": "file:../common", + "@types/duo_web_sdk": "^2.7.1", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zone.js": "0.11.4" + } + }, + "@bitwarden/jslib-common": { + "version": "file:common", + "requires": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zxcvbn": "^4.4.2" + } + }, + "@bitwarden/jslib-electron": { + "version": "file:electron", + "requires": { + "@bitwarden/jslib-common": "file:../common", + "@types/node": "^16.11.12", + "electron": "16.1.0", + "electron-log": "4.4.6", + "electron-store": "8.0.1", + "electron-updater": "5.0.0", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "@bitwarden/jslib-node": { + "version": "file:node", + "requires": { + "@bitwarden/jslib-common": "file:../common", + "@types/inquirer": "^7.3.1", + "@types/lowdb": "^1.0.10", + "@types/node": "^16.11.12", + "@types/node-fetch": "^2.5.10", + "chalk": "^4.1.1", + "commander": "7.2.0", + "form-data": "4.0.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.0.0", + "lowdb": "1.0.0", + "node-fetch": "^2.6.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "inquirer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, + "@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", + "dev": true + }, + "@discoveryjs/json-ext": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", + "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "dev": true + }, + "@electron/get": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^3.0.0", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@eslint/eslintrc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@fluffy-spoon/substitute": { + "version": "1.208.0", + "resolved": "https://registry.npmjs.org/@fluffy-spoon/substitute/-/substitute-1.208.0.tgz", + "integrity": "sha512-BU5vKRoK4OYlKzDtyg4HbtWnUNLOvV0ntqEZIphz+mq2G0HlVFywwJ7M+FbIcnJVDbUReS01FyL5x8R01r7zBg==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dev": true, + "requires": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@jridgewell/resolve-uri": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-1.0.0.tgz", + "integrity": "sha512-9oLAnygRMi8Q5QkYEU4XWK04B+nuoXoxjRvRxgjuChkLZFBja0YPSgdZ7dZtwhncLBcQe/I/E+fLuk5qxcYVJA==", + "dev": true + }, + "@jsdevtools/coverage-istanbul-loader": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", + "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", + "dev": true, + "requires": { + "convert-source-map": "^1.7.0", + "istanbul-lib-instrument": "^4.0.3", + "loader-utils": "^2.0.0", + "merge-source-map": "^1.1.0", + "schema-utils": "^2.7.0" + } + }, + "@microsoft/signalr": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz", + "integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==", + "requires": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.7.3", + "node-fetch": "^2.6.0", + "ws": "^6.0.0" + }, + "dependencies": { + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "@microsoft/signalr-protocol-msgpack": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz", + "integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==", + "requires": { + "@microsoft/signalr": ">=5.0.10", + "msgpack5": "^4.5.0" + } + }, + "@ngtools/webpack": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.17.tgz", + "integrity": "sha512-uaS+2YZgPDW3VmUuwh4/yfIFV1KRVGWefc6xLWIqKRKs6mlRYs65m3ib9dX7CTS4kQMCbhxkxMbpBO2yXlzfvA==", + "dev": true, + "requires": {} + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/duo_web_sdk": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz", + "integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==", + "dev": true + }, + "@types/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dev": true, + "requires": { + "@types/through": "*", + "rxjs": "^6.4.0" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "27.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", + "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "dev": true, + "requires": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/lodash": { + "version": "4.14.181", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.181.tgz", + "integrity": "sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==", + "dev": true + }, + "@types/lowdb": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/lowdb/-/lowdb-1.0.11.tgz", + "integrity": "sha512-h99VMxvTuz+VsXUVCCJo4dsps4vbkXwvU71TpmxDoiBU24bJ0VBygIHgmMm+UPoQIFihmV6euRik4z8J7XDJWg==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lunr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", + "integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "@types/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/node-forge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/prettier": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", + "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", + "dev": true + }, + "@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==" + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==", + "dev": true + }, + "@types/webpack-sources": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.9.tgz", + "integrity": "sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@types/zxcvbn": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz", + "integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "debug": "^4.3.2" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true, + "requires": {} + }, + "ajv-formats": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.0.tgz", + "integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "requires": { + "string-width": "^4.1.0" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==" + }, + "autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dev": true, + "requires": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-loader": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", + "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz", + "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.4", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz", + "integrity": "sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.2", + "core-js-compat": "^3.16.2" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz", + "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.4" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "optional": true + }, + "boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-hrtime": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", + "integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA==" + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "builder-util-runtime": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.0.0.tgz", + "integrity": "sha512-SkpEtSmTkREDHRJnxKEv43aAYp8sYWY8fxYBhGLBLOBIRXeaIp6Kv3lBgSD7uR8jQtC7CA659sqJrpSV6zNvSA==", + "requires": { + "debug": "^4.3.2", + "sax": "^1.2.4" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz", + "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001325", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz", + "integrity": "sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==", + "dev": true + }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "dev": true + }, + "circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "requires": {} + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "conf": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.1.2.tgz", + "integrity": "sha512-o9Fv1Mv+6A0JpoayQ8JleNp3hhkbOJP/Re/Q+QqxMPHPkABVsRjQGWZn9A5GcqLiTNC6d89p2PB5ZhHVDSMwyg==", + "requires": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, + "dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "requires": { + "is-obj": "^2.0.0" + } + } + } + }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + } + } + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "requires": { + "is-what": "^3.14.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz", + "integrity": "sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw==", + "dev": true, + "requires": { + "fast-glob": "^3.2.5", + "glob-parent": "^6.0.0", + "globby": "^11.0.3", + "normalize-path": "^3.0.0", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "core-js": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz", + "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==", + "dev": true + }, + "core-js-compat": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "dev": true, + "requires": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "critters": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz", + "integrity": "sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "css-select": "^4.1.3", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + }, + "dependencies": { + "postcss": { + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "dev": true, + "requires": { + "nanoid": "^3.3.1", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-declaration-sorter": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", + "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==", + "dev": true, + "requires": {} + }, + "css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.2.0.tgz", + "integrity": "sha512-/rvHfYRjIpymZblf49w8jYcRo2y9gj6rV8UroHGmBxKrIyGLokpycyKzp9OkitvqT29ZSpzJ0Ic7SpnJX3sC8g==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + } + }, + "css-minimizer-webpack-plugin": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.2.tgz", + "integrity": "sha512-B3I5e17RwvKPJwsxjjWcdgpU/zqylzK1bPVghcmpFHRL48DXiBgrtqz1BJsn68+t/zzaLp9kYAaEDvQ7GyanFQ==", + "dev": true, + "requires": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "p-limit": "^3.0.2", + "postcss": "^8.3.5", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", + "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", + "dev": true, + "requires": { + "css": "^2.0.0" + } + }, + "css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.7.tgz", + "integrity": "sha512-pVsUV6LcTXif7lvKKW9ZrmX+rGRzxkEdJuVJcp5ftUjWITgwam5LMZOgaTvUrWPkcORBey6he7JKb4XAJvrpKg==", + "dev": true, + "requires": { + "cssnano-preset-default": "^5.2.7", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz", + "integrity": "sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^6.2.2", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.0", + "postcss-discard-comments": "^5.1.1", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.4", + "postcss-merge-rules": "^5.1.1", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.2", + "postcss-minify-selectors": "^5.2.0", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.0", + "postcss-normalize-repeat-style": "^5.1.0", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.1", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.1.2" + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "dev": true + }, + "debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "requires": { + "mimic-fn": "^3.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==" + } + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "devOptional": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "devOptional": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "duo_web_sdk": { + "version": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda", + "from": "duo_web_sdk@git+https://github.com/duosecurity/duo_web_sdk.git" + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-16.1.0.tgz", + "integrity": "sha512-G4fpHmE3sgd497e0zEier/AmZ4fyReX8ozYAl468+FaI5kb44+69igRHQwRUtmPzv+fCn/Jm4wJQPfLe60WmUQ==", + "requires": { + "@electron/get": "^1.13.0", + "@types/node": "^14.6.2", + "extract-zip": "^1.0.3" + }, + "dependencies": { + "@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" + } + } + }, + "electron-log": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-4.4.6.tgz", + "integrity": "sha512-nirYgRdY+F+vclr8ijdwy2vW03IzFpDHTaKNWu76dEN21Y76+smcES5knS7cgHUUB0qNLOi8vZO36taakjbSXA==" + }, + "electron-store": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.1.tgz", + "integrity": "sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==", + "requires": { + "conf": "^10.0.3", + "type-fest": "^1.0.2" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" + } + } + }, + "electron-to-chromium": { + "version": "1.4.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", + "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", + "dev": true + }, + "electron-updater": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.0.0.tgz", + "integrity": "sha512-SC3sw92ewjrJFZIJamVOHqxW3yzFin/Q/Swf2FZodqm9xd4s8hCbPCfptpD/xBIcvQmAv2BAggbprwWq/fyp6w==", + "requires": { + "@types/semver": "^7.3.6", + "builder-util-runtime": "9.0.0", + "fs-extra": "^10.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.3.5" + }, + "dependencies": { + "fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "devOptional": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", + "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true, + "peer": true + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-denodeify": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz", + "integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8=" + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "esbuild": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.8.tgz", + "integrity": "sha512-A4af7G7YZLfG5OnARJRMtlpEsCkq/zHZQXewgPA864l9D6VjjbH1SuFYK/OSV6BtHwDGkdwyRrX0qQFLnMfUcw==", + "dev": true, + "optional": true, + "requires": { + "esbuild-android-arm64": "0.13.8", + "esbuild-darwin-64": "0.13.8", + "esbuild-darwin-arm64": "0.13.8", + "esbuild-freebsd-64": "0.13.8", + "esbuild-freebsd-arm64": "0.13.8", + "esbuild-linux-32": "0.13.8", + "esbuild-linux-64": "0.13.8", + "esbuild-linux-arm": "0.13.8", + "esbuild-linux-arm64": "0.13.8", + "esbuild-linux-mips64le": "0.13.8", + "esbuild-linux-ppc64le": "0.13.8", + "esbuild-netbsd-64": "0.13.8", + "esbuild-openbsd-64": "0.13.8", + "esbuild-sunos-64": "0.13.8", + "esbuild-windows-32": "0.13.8", + "esbuild-windows-64": "0.13.8", + "esbuild-windows-arm64": "0.13.8" + } + }, + "esbuild-android-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz", + "integrity": "sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz", + "integrity": "sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz", + "integrity": "sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz", + "integrity": "sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz", + "integrity": "sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz", + "integrity": "sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz", + "integrity": "sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz", + "integrity": "sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz", + "integrity": "sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz", + "integrity": "sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz", + "integrity": "sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.11.tgz", + "integrity": "sha512-DoThrkzunZ1nfRGoDN6REwmo8ZZWHd2ztniPVIR5RMw/Il9wiWEYBahb8jnMzQaSOxBsGp0PbyJeVLTUatnlcw==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz", + "integrity": "sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz", + "integrity": "sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz", + "integrity": "sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.13.8.tgz", + "integrity": "sha512-UbD+3nloiSpJWXTCInZQrqPe8Y+RLfDkY/5kEHiXsw/lmaEvibe69qTzQu16m5R9je/0bF7VYQ5jaEOq0z9lLA==", + "dev": true + }, + "esbuild-windows-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz", + "integrity": "sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz", + "integrity": "sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz", + "integrity": "sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "devOptional": true + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "requires": { + "original": "^1.0.0" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + } + }, + "express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + } + } + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "fetch-cookie": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz", + "integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==", + "requires": { + "es6-denodeify": "^0.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + } + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "dev": true + }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + } + }, + "global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "requires": { + "ini": "2.0.0" + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "optional": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, + "hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "requires": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "requires": {} + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + }, + "inquirer": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", + "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.3.0", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + } + } + }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + }, + "dependencies": { + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + } + } + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "requires": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + } + }, + "jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + } + }, + "jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dev": true, + "requires": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "dependencies": { + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dev": true, + "requires": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + } + }, + "jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true + }, + "jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + } + }, + "jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dev": true, + "requires": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-preset-angular": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-11.1.1.tgz", + "integrity": "sha512-ZlYiKJhAQSU9wIjncX59xutcj49R4MiDsTPSwZiwdTAHQvHm32MS6SGimQIVBqh1DukfwYX0NXKS0D/onLAsLQ==", + "dev": true, + "requires": { + "bs-logger": "^0.2.6", + "esbuild": "0.14.11", + "esbuild-wasm": "0.14.11", + "jest-environment-jsdom": "^27.0.0", + "pretty-format": "^27.0.0", + "ts-jest": "^27.0.0" + }, + "dependencies": { + "esbuild": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.11.tgz", + "integrity": "sha512-xZvPtVj6yecnDeFb3KjjCM6i7B5TCAQZT77kkW/CpXTMnd6VLnRPKrUB1XHI1pSq6a4Zcy3BGueQ8VljqjDGCg==", + "dev": true, + "optional": true, + "requires": { + "esbuild-android-arm64": "0.14.11", + "esbuild-darwin-64": "0.14.11", + "esbuild-darwin-arm64": "0.14.11", + "esbuild-freebsd-64": "0.14.11", + "esbuild-freebsd-arm64": "0.14.11", + "esbuild-linux-32": "0.14.11", + "esbuild-linux-64": "0.14.11", + "esbuild-linux-arm": "0.14.11", + "esbuild-linux-arm64": "0.14.11", + "esbuild-linux-mips64le": "0.14.11", + "esbuild-linux-ppc64le": "0.14.11", + "esbuild-linux-s390x": "0.14.11", + "esbuild-netbsd-64": "0.14.11", + "esbuild-openbsd-64": "0.14.11", + "esbuild-sunos-64": "0.14.11", + "esbuild-windows-32": "0.14.11", + "esbuild-windows-64": "0.14.11", + "esbuild-windows-arm64": "0.14.11" + } + }, + "esbuild-android-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.11.tgz", + "integrity": "sha512-6iHjgvMnC/SzDH8TefL+/3lgCjYWwAd1LixYfmz/TBPbDQlxcuSkX0yiQgcJB9k+ibZ54yjVXziIwGdlc+6WNw==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.11.tgz", + "integrity": "sha512-olq84ikh6TiBcrs3FnM4eR5VPPlcJcdW8BnUz/lNoEWYifYQ+Po5DuYV1oz1CTFMw4k6bQIZl8T3yxL+ZT2uvQ==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.11.tgz", + "integrity": "sha512-Jj0ieWLREPBYr/TZJrb2GFH8PVzDqiQWavo1pOFFShrcmHWDBDrlDxPzEZ67NF/Un3t6sNNmeI1TUS/fe1xARg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.11.tgz", + "integrity": "sha512-C5sT3/XIztxxz/zwDjPRHyzj/NJFOnakAanXuyfLDwhwupKPd76/PPHHyJx6Po6NI6PomgVp/zi6GRB8PfrOTA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.11.tgz", + "integrity": "sha512-y3Llu4wbs0bk4cwjsdAtVOesXb6JkdfZDLKMt+v1U3tOEPBdSu6w8796VTksJgPfqvpX22JmPLClls0h5p+L9w==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.11.tgz", + "integrity": "sha512-Cg3nVsxArjyLke9EuwictFF3Sva+UlDTwHIuIyx8qpxRYAOUTmxr2LzYrhHyTcGOleLGXUXYsnUVwKqnKAgkcg==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.11.tgz", + "integrity": "sha512-oeR6dIrrojr8DKVrxtH3xl4eencmjsgI6kPkDCRIIFwv4p+K7ySviM85K66BN01oLjzthpUMvBVfWSJkBLeRbg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.11.tgz", + "integrity": "sha512-vcwskfD9g0tojux/ZaTJptJQU3a7YgTYsptK1y6LQ/rJmw7U5QJvboNawqM98Ca3ToYEucfCRGbl66OTNtp6KQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.11.tgz", + "integrity": "sha512-+e6ZCgTFQYZlmg2OqLkg1jHLYtkNDksxWDBWNtI4XG4WxuOCUErLqfEt9qWjvzK3XBcCzHImrajkUjO+rRkbMg==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.11.tgz", + "integrity": "sha512-Rrs99L+p54vepmXIb87xTG6ukrQv+CzrM8eoeR+r/OFL2Rg8RlyEtCeshXJ2+Q66MXZOgPJaokXJZb9snq28bw==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.11.tgz", + "integrity": "sha512-JyzziGAI0D30Vyzt0HDihp4s1IUtJ3ssV2zx9O/c+U/dhUHVP2TmlYjzCfCr2Q6mwXTeloDcLS4qkyvJtYptdQ==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.11.tgz", + "integrity": "sha512-12luoRQz+6eihKYh1zjrw0CBa2aw3twIiHV/FAfjh2NEBDgJQOY4WCEUEN+Rgon7xmLh4XUxCQjnwrvf8zhACw==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.11.tgz", + "integrity": "sha512-l18TZDjmvwW6cDeR4fmizNoxndyDHamGOOAenwI4SOJbzlJmwfr0jUgjbaXCUuYVOA964siw+Ix+A+bhALWg8Q==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.11.tgz", + "integrity": "sha512-bmYzDtwASBB8c+0/HVOAiE9diR7+8zLm/i3kEojUH2z0aIs6x/S4KiTuT5/0VKJ4zk69kXel1cNWlHBMkmavQg==", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.11.tgz", + "integrity": "sha512-9e1R6hv0hiU+BkJI2edqUuWfXUbOP2Mox+Ijl/uY1vLLlSsunkrcADqD/4Rz+VCEDzw6ecscJM+uJqR2fRmEUg==", + "dev": true + }, + "esbuild-windows-32": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.11.tgz", + "integrity": "sha512-J1Ys5hMid8QgdY00OBvIolXgCQn1ARhYtxPnG6ESWNTty3ashtc4+As5nTrsErnv8ZGUcWZe4WzTP/DmEVX1UQ==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.11.tgz", + "integrity": "sha512-h9FmMskMuGeN/9G9+LlHPAoiQk9jlKDUn9yA0MpiGzwLa82E7r1b1u+h2a+InprbSnSLxDq/7p5YGtYVO85Mlg==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.11.tgz", + "integrity": "sha512-dZp7Krv13KpwKklt9/1vBFBMqxEQIO6ri7Azf8C+ob4zOegpJmha2XY9VVWP/OyQ0OWk6cEeIzMJwInRZrzBUQ==", + "dev": true, + "optional": true + } + } + }, + "jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true + }, + "jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + } + }, + "jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dev": true, + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + } + }, + "jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dev": true, + "requires": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" + }, + "less": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz", + "integrity": "sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw==", + "dev": true, + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "less-loader": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.0.1.tgz", + "integrity": "sha512-Crln//HpW9M5CbtdfWm3IO66Cvx1WhZQvNybXgfB2dD/6Sav9ppw+IWqs/FQKPBFO4B6X0X28Z0WNznshgwUzA==", + "dev": true, + "requires": { + "klona": "^2.0.4" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "license-webpack-plugin": { + "version": "2.3.20", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz", + "integrity": "sha512-AHVueg9clOKACSHkhmEI+PCC9x8+qsQVuKECZD3ETxETK5h/PCv5/MUzyG1gm8OMcip/s1tcNxqo9Qb7WhjGsg==", + "dev": true, + "requires": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.0" + } + }, + "lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "lint-staged": { + "version": "12.3.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.3.7.tgz", + "integrity": "sha512-/S4D726e2GIsDVWIk1XGvheCaDm1SJRQp8efamZFWJxQMVEbOwSysp7xb49Oo73KYCdy97mIWinhlxcoNqIfIQ==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.16", + "commander": "^8.3.0", + "debug": "^4.3.3", + "execa": "^5.1.1", + "lilconfig": "2.0.4", + "listr2": "^4.0.1", + "micromatch": "^4.0.4", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.0", + "pidtree": "^0.5.0", + "string-argv": "^0.3.1", + "supports-color": "^9.2.1", + "yaml": "^1.10.2" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + }, + "lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true + }, + "supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true + } + } + }, + "listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true + }, + "lowdb": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "requires": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "requires": { + "escape-string-regexp": "^4.0.0" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", + "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true + } + } + }, + "memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "mini-css-extract-plugin": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.2.tgz", + "integrity": "sha512-ZmqShkn79D36uerdED+9qdo1ZYG8C1YsWvXu0UMJxurZnSdgz7gQKO2EGv8T55MhDqG3DYmGtizZNpM/UbTlcA==", + "dev": true, + "requires": { + "schema-utils": "^3.1.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "msgpack5": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", + "integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==", + "requires": { + "bl": "^2.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.3.6", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true + }, + "nanoid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", + "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "dev": true, + "optional": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true + }, + "nodemon": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", + "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5", + "update-notifier": "^5.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "devOptional": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", + "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + }, + "dependencies": { + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + } + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + }, + "dependencies": { + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "requires": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pidtree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "piscina": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.1.0.tgz", + "integrity": "sha512-KTW4sjsCD34MHrUbx9eAAbuUSpVj407hQSgk/6Epkg0pbRBmv4a3UX7Sr8wxm9xYqQLnsN4mFOjqGDzHAdgKQg==", + "dev": true, + "requires": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0", + "nice-napi": "^1.0.2" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + } + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", + "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + }, + "dependencies": { + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + } + } + }, + "postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "dev": true, + "requires": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz", + "integrity": "sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "dev": true, + "requires": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-discard-comments": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz", + "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==", + "dev": true, + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "requires": {} + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "requires": {} + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "requires": {} + }, + "postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "dev": true, + "requires": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-font-variant": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz", + "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-import": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", + "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", + "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-loader": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.1.1.tgz", + "integrity": "sha512-lBmJMvRh1D40dqpWKr9Rpygwxn8M74U9uaCSeYGNKLGInbk9mXBt1ultHf2dH9Ghk6Ue4UXlXWwGMH9QdUJ5ug==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "semver": "^7.3.5" + } + }, + "postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-merge-longhand": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", + "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + } + }, + "postcss-merge-rules": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz", + "integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz", + "integrity": "sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz", + "integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz", + "integrity": "sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz", + "integrity": "sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-ordered-values": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz", + "integrity": "sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==", + "dev": true, + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "dev": true, + "requires": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-selector-not": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz", + "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "dev": true, + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postinstall-build": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.3.tgz", + "integrity": "sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, + "qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + } + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.36.0.tgz", + "integrity": "sha512-fQzEjipfOv5kh930nu3Imzq3ie/sGDc/4KtQMJlt7RRdrkQSfe37Bwi/Rf/gfuYHsIuE1fIlDMvpyMcEwjnPvg==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz", + "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + }, + "dependencies": { + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + } + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "requires": { + "type-fest": "^0.13.1" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + } + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "sockjs-client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz", + "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "eventsource": "^1.1.0", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true + }, + "source-map-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.0.tgz", + "integrity": "sha512-GKGWqWvYr04M7tn8dryIWvb0s8YM41z82iQv01yBtIylgxax0CwvSy6gc2Y02iuXwEfGWRlMicH0nvms9UZphw==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "source-map-js": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "steno": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "requires": { + "graceful-fs": "^4.1.3" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + } + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "style-loader": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.2.1.tgz", + "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==", + "dev": true, + "requires": {} + }, + "stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + } + }, + "stylus": { + "version": "0.54.8", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", + "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", + "dev": true, + "requires": { + "css-parse": "~2.0.0", + "debug": "~3.1.0", + "glob": "^7.1.6", + "mkdirp": "~1.0.4", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "semver": "^6.3.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "stylus-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.1.0.tgz", + "integrity": "sha512-qKO34QCsOtSJrXxQQmXsPeaVHh6hMumBAFIoJTcsSr2VzrA6o/CW9HCGR8spCjzJhN8oKQHdj/Ytx0wwXyElkw==", + "dev": true, + "requires": { + "fast-glob": "^3.2.5", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "requires": { + "debug": "^4.1.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "terser": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", + "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz", + "integrity": "sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==", + "dev": true, + "requires": { + "jest-worker": "^27.0.2", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "requires": { + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "ts-jest": { + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + } + }, + "ts-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.3.0.tgz", + "integrity": "sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + } + }, + "ts-node": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", + "yn": "3.1.1" + }, + "dependencies": { + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + } + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "ttypescript": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", + "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", + "dev": true, + "requires": { + "resolve": ">=1.9.0" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typemoq": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typemoq/-/typemoq-2.1.0.tgz", + "integrity": "sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "lodash": "^4.17.4", + "postinstall-build": "^5.0.1" + } + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "typescript-transform-paths": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/typescript-transform-paths/-/typescript-transform-paths-2.2.4.tgz", + "integrity": "sha512-i+/sgp3rw1ZronMCm2TKGBy1dlvN88Kd8CCb+HWnOE8+Hv0uIVnbC8xM5AD2t1JBCWabEhuH9p3n8DOVi0+R6g==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + } + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "requires": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, + "v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "webpack": { + "version": "5.71.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.71.0.tgz", + "integrity": "sha512-g4dFT7CFG8LY0iU5G8nBL6VlkT21Z7dcYDpJAEJV5Q1WLb9UwnFbrem1k7K52ILqEmomN7pnzWFxxE6SlDY56A==", + "dev": true, + "peer": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.2", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peer": true, + "requires": {} + }, + "enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "peer": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "peer": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "peer": true + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "peer": true + } + } + }, + "webpack-dev-middleware": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.0.0.tgz", + "integrity": "sha512-9zng2Z60pm6A98YoRcA0wSxw1EYn7B7y5owX/Tckyt9KGyULTkLtiavjaXlWqOMkM0YtqGgL3PvMOFgyFLq8vw==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "mem": "^8.1.1", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-dev-server": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", + "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==", + "dev": true, + "requires": { + "ansi-html-community": "0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.2.tgz", + "integrity": "sha512-GBWYBoyalbo5YClwWop9qe6Zclp8CIXYGIz12OPclJhIrSplDxs1Ls1JDMH8xBPPrg1T6ISaTW9Y6zOrwEiAzw==", + "dev": true, + "requires": { + "webpack-sources": "^1.3.0" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "dev": true, + "requires": {} + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, + "zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "requires": { + "tslib": "^2.0.0" + } + }, + "zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + } + } +} diff --git a/jslib/package.json b/jslib/package.json new file mode 100644 index 00000000..96a3cc93 --- /dev/null +++ b/jslib/package.json @@ -0,0 +1,74 @@ +{ + "name": "@bitwarden/jslib", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "lint": "eslint . && prettier --check .", + "lint:fix": "eslint . --fix", + "prettier": "prettier --write .", + "test": "jest", + "test:watch": "jest --watch", + "test:watch:all": "jest --watchAll", + "prepare": "husky install" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^12.2.13", + "@angular/compiler-cli": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@fluffy-spoon/substitute": "^1.202.0", + "@types/jest": "^27.4.1", + "@types/node": "^16.11.12", + "@typescript-eslint/eslint-plugin": "^5.10.1", + "@typescript-eslint/parser": "^5.10.1", + "commander": "7.2.0", + "concurrently": "^6.1.0", + "eslint": "^8.7.0", + "eslint-config-prettier": "^8.3.0", + "eslint-import-resolver-typescript": "^2.5.0", + "eslint-plugin-import": "^2.25.4", + "form-data": "4.0.0", + "husky": "^7.0.4", + "jest": "^27.5.1", + "jest-preset-angular": "^11.1.1", + "jsdom": "^16.5.3", + "lint-staged": "^12.1.2", + "node-forge": "^1.2.0", + "nodemon": "^2.0.7", + "prettier": "2.5.1", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "ts-loader": "^8.1.0", + "ts-node": "^10.4.0", + "tsconfig-paths": "^3.12.0", + "ttypescript": "^1.5.12", + "typemoq": "^2.1.0", + "typescript": "4.3.5", + "typescript-transform-paths": "^2.2.3", + "zone.js": "0.11.4" + }, + "dependencies": { + "@bitwarden/jslib-angular": "file:angular", + "@bitwarden/jslib-common": "file:common", + "@bitwarden/jslib-electron": "file:electron", + "@bitwarden/jslib-node": "file:node" + }, + "engines": { + "node": "~16", + "npm": "~8" + }, + "lint-staged": { + "*": "prettier --ignore-unknown --write", + "*.ts": "eslint --fix" + } +} diff --git a/jslib/shared/eslintrc.json b/jslib/shared/eslintrc.json new file mode 100644 index 00000000..70c61bc7 --- /dev/null +++ b/jslib/shared/eslintrc.json @@ -0,0 +1,29 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + "prettier" + ], + "rules": { + "@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled + "@typescript-eslint/no-unused-vars": ["warn", { "args": "none" }], + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { + "accessibility": "no-public" + } + ], + "@typescript-eslint/no-this-alias": [ + "error", + { + "allowedNames": ["self"] + } + ], + "no-console": "warn", + "import/no-unresolved": "off" // TODO: Look into turning off once each package is an actual package. + } +} diff --git a/jslib/shared/tsconfig.json b/jslib/shared/tsconfig.json new file mode 100644 index 00000000..3a70f6b9 --- /dev/null +++ b/jslib/shared/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "pretty": true, + "moduleResolution": "node", + "noImplicitAny": true, + "target": "ES6", + "module": "commonjs", + "lib": ["es5", "es6", "es7", "dom"], + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "outDir": "dist" + } +} diff --git a/jslib/tsconfig.json b/jslib/tsconfig.json new file mode 100644 index 00000000..63b3da4e --- /dev/null +++ b/jslib/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "pretty": true, + "moduleResolution": "node", + "noImplicitAny": true, + "target": "ES6", + "module": "commonjs", + "lib": ["es5", "es6", "es7", "dom"], + "sourceMap": true, + "declaration": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "declarationDir": "dist/types", + "outDir": "dist", + "baseUrl": ".", + "paths": { + "jslib-common/*": ["common/src/*"], + "jslib-angular/*": ["angular/src/*"], + "jslib-electron/*": ["electron/src/*"], + "jslib-node/*": ["node/src/*"] + }, + "plugins": [ + { + "transform": "typescript-transform-paths" + } + ] + }, + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/tsconfig.spec.json b/jslib/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 890aac20..7e520b70 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -6,7 +6,19 @@ import { FormsModule } from "@angular/forms"; import { BrowserModule } from "@angular/platform-browser"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { JslibModule } from "jslib-angular/jslib.module"; +import { CalloutComponent } from "jslib-angular/components/callout.component"; +import { IconComponent } from "jslib-angular/components/icon.component"; +import { BitwardenToastModule } from "jslib-angular/components/toastr.component"; +import { A11yTitleDirective } from "jslib-angular/directives/a11y-title.directive"; +import { ApiActionDirective } from "jslib-angular/directives/api-action.directive"; +import { AutofocusDirective } from "jslib-angular/directives/autofocus.directive"; +import { BlurClickDirective } from "jslib-angular/directives/blur-click.directive"; +import { BoxRowDirective } from "jslib-angular/directives/box-row.directive"; +import { FallbackSrcDirective } from "jslib-angular/directives/fallback-src.directive"; +import { StopClickDirective } from "jslib-angular/directives/stop-click.directive"; +import { StopPropDirective } from "jslib-angular/directives/stop-prop.directive"; +import { I18nPipe } from "jslib-angular/pipes/i18n.pipe"; +import { SearchCiphersPipe } from "jslib-angular/pipes/search-ciphers.pipe"; import { ApiKeyComponent } from "./accounts/apiKey.component"; import { EnvironmentComponent } from "./accounts/environment.component"; @@ -18,23 +30,38 @@ import { MoreComponent } from "./tabs/more.component"; import { SettingsComponent } from "./tabs/settings.component"; import { TabsComponent } from "./tabs/tabs.component"; - @NgModule({ imports: [ - AppRoutingModule, - BrowserAnimationsModule, BrowserModule, + BrowserAnimationsModule, FormsModule, - JslibModule, + AppRoutingModule, ServicesModule, + BitwardenToastModule.forRoot({ + maxOpened: 5, + autoDismiss: true, + closeButton: true, + }), ], declarations: [ + A11yTitleDirective, + ApiActionDirective, ApiKeyComponent, AppComponent, + AutofocusDirective, + BlurClickDirective, + BoxRowDirective, + CalloutComponent, DashboardComponent, EnvironmentComponent, + FallbackSrcDirective, + I18nPipe, + IconComponent, MoreComponent, + SearchCiphersPipe, SettingsComponent, + StopClickDirective, + StopPropDirective, TabsComponent, ], providers: [], diff --git a/src/app/services/services.module.ts b/src/app/services/services.module.ts index 192b0286..db41029b 100644 --- a/src/app/services/services.module.ts +++ b/src/app/services/services.module.ts @@ -1,4 +1,4 @@ -import { APP_INITIALIZER, Injector, NgModule } from "@angular/core"; +import { APP_INITIALIZER, NgModule } from "@angular/core"; import { JslibServicesModule } from "jslib-angular/services/jslib-services.module"; import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service";